Changeset 190697 in webkit


Ignore:
Timestamp:
Oct 7, 2015, 5:33:55 PM (10 years ago)
Author:
mmaxfield@apple.com
Message:

Test font-variant-* and font-feature-settings with TrueType fonts
https://bugs.webkit.org/show_bug.cgi?id=149776

Reviewed by Simon Fraser.

Tools:

This test extends our existing FontWithFeatures project to be able to generate a
TrueType font. This font is conceptually similar as the existing OpenType font,
except the feature -> character mapping is different.

The font itself only supports the following characters:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
However, the shape of these letters are either an X or a check mark.
The letter "A" always is a check mark.
The letter "B" always is an X.
Each font feature has an letter associated with it. When the font feature is enabled,
that letter is shown as a check mark. For example, when
"kLowerCaseType / kLowerCaseSmallCapsSelector" is enabled, "S" is shown as a check
mark.

Here are the mappings of font features to letters:
kLigaturesType / kCommonLigaturesOnSelector: C
kLigaturesType / kContextualLigaturesOnSelector: D
kLigaturesType / kRareLigaturesOnSelector: G
kLigaturesType / kHistoricalLigaturesOnSelector: I
kContextualAlternatesType / kContextualAlternatesOnSelector: L
kVerticalPositionType / kInferiorsSelector: O
kVerticalPositionType / kSuperiorsSelector: P
kLowerCaseType / kLowerCaseSmallCapsSelector: S
kUpperCaseType / kUpperCaseSmallCapsSelector: V
kLowerCaseType / kLowerCasePetiteCapsSelector: T
kUpperCaseType / kUpperCasePetiteCapsSelector: W
kLetterCaseType / 14: Y
kStyleOptionsType / kTitlingCapsSelector: a
kNumberCaseType / kUpperCaseNumbersSelector: c
kNumberCaseType / kLowerCaseNumbersSelector: d
kNumberSpacingType / kProportionalNumbersSelector: f
kNumberSpacingType / kMonospacedNumbersSelector: g
kFractionsType / kDiagonalFractionsSelector: i
kFractionsType / kVerticalFractionsSelector: j
kVerticalPositionType / kOrdinalsSelector: Q
kTypographicExtrasType / kSlashedZeroOnSelector: k
kLigaturesType / kHistoricalLigaturesOnSelector: K
kCharacterShapeType / kJIS1978CharactersSelector: m
kCharacterShapeType / kJIS1983CharactersSelector: n
kCharacterShapeType / kJIS1990CharactersSelector: o
kCharacterShapeType / kJIS2004CharactersSelector: p
kCharacterShapeType / kSimplifiedCharactersSelector: q
kCharacterShapeType / kTraditionalCharactersSelector: r
kTextSpacingType / kMonospacedTextSelector: t
kTextSpacingType / kProportionalTextSelector: u
kRubyKanaType / kRubyKanaOnSelector: v

  • FontWithFeatures/FontWithFeatures.xcodeproj/project.pbxproj:
  • FontWithFeatures/FontWithFeatures/FontCreator.cpp:

(CFFBuilder::moveTo):
(CFFBuilder::lineTo):
(GLYFBuilder::GLYFBuilder):
(GLYFBuilder::takeResult):
(GLYFBuilder::moveTo):
(GLYFBuilder::lineTo):
(GLYFBuilder::closePath):
(GLYFBuilder::writePoint):
(GLYFBuilder::append16):
(generateBoxCharString):
(generateCheckCharString):
(generateXCharString):
(itemForGlyph):
(Generator::generate):
(Generator::insertSelector):
(Generator::insertFeature):
(Generator::generateFeatureDescription):
(Generator::appendCFFTable):
(Generator::appendGLYFTable):
(Generator::appendLOCATable):
(Generator::appendFEATTable):
(Generator::appendMetamorphosisChain):
(Generator::appendMORXTable):
(Generator::appendHEADTable):
(Generator::appendHMTXTable):
(Generator::appendNameSubtable):
(Generator::append2ByteASCIIString):
(Generator::appendNAMETable):
(generateFont):
(CFFBuilder::curveToCubic): Deleted.
(charStringForGlyph): Deleted.

  • FontWithFeatures/FontWithFeatures/FontCreator.h:
  • FontWithFeatures/FontWithFeatures/main.cpp:

(constructFontWithTrueTypeFeature):
(constructFontWithOpenTypeFeature):
(drawText):
(main):
(drawTextWithFeature): Deleted.

LayoutTests:

  • css3/font-feature-settings-rendering-2-expected.html:
  • css3/font-feature-settings-rendering-2.html:
  • css3/resources/FontWithFeatures.ttf: Added.
Location:
trunk
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r190695 r190697  
     12015-10-07  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Test font-variant-* and font-feature-settings with TrueType fonts
     4        https://bugs.webkit.org/show_bug.cgi?id=149776
     5
     6        Reviewed by Simon Fraser.
     7
     8        * css3/font-feature-settings-rendering-2-expected.html:
     9        * css3/font-feature-settings-rendering-2.html:
     10        * css3/resources/FontWithFeatures.ttf: Added.
     11
    1122015-10-07  Mark Lam  <mark.lam@apple.com>
    213
  • trunk/LayoutTests/css3/font-feature-settings-rendering-2-expected.html

    r190192 r190697  
    66    font-family: "FontFeaturesTest";
    77    src: url("resources/FontWithFeatures.otf") format("opentype");
     8}
     9@font-face {
     10    font-family: "FontFeaturesTestTTF";
     11    src: url("resources/FontWithFeatures.ttf") format("truetype");
    812}
    913</style>
     
    4448<span style="font-family: FontFeaturesTest;">BA</span>
    4549</div>
     50<div><span style="font-family: FontFeaturesTestTTF;">BA</span>
     51<span style="font-family: FontFeaturesTestTTF;">BA</span>
     52<span style="font-family: FontFeaturesTestTTF;">BA</span>
     53<span style="font-family: FontFeaturesTestTTF;">BA</span>
     54<span style="font-family: FontFeaturesTestTTF;">BA</span>
     55<span style="font-family: FontFeaturesTestTTF;">BA</span>
     56<span style="font-family: FontFeaturesTestTTF;">BA</span>
     57<span style="font-family: FontFeaturesTestTTF;">BA</span>
     58<span style="font-family: FontFeaturesTestTTF;">BA</span>
     59<span style="font-family: FontFeaturesTestTTF;">BA</span>
     60<span style="font-family: FontFeaturesTestTTF;">BA</span>
     61<span style="font-family: FontFeaturesTestTTF;">BA</span>
     62<span style="font-family: FontFeaturesTestTTF;">BA</span>
     63<span style="font-family: FontFeaturesTestTTF;">BA</span>
     64<span style="font-family: FontFeaturesTestTTF;">BA</span>
     65<span style="font-family: FontFeaturesTestTTF;">BA</span>
     66<span style="font-family: FontFeaturesTestTTF;">BA</span>
     67<span style="font-family: FontFeaturesTestTTF;">BA</span>
     68<span style="font-family: FontFeaturesTestTTF;">BA</span>
     69<span style="font-family: FontFeaturesTestTTF;">BA</span>
     70<span style="font-family: FontFeaturesTestTTF;">BA</span>
     71<span style="font-family: FontFeaturesTestTTF;">BA</span>
     72<span style="font-family: FontFeaturesTestTTF;">BA</span>
     73<span style="font-family: FontFeaturesTestTTF;">BA</span>
     74<span style="font-family: FontFeaturesTestTTF;">BA</span>
     75<span style="font-family: FontFeaturesTestTTF;">BA</span>
     76<span style="font-family: FontFeaturesTestTTF;">BA</span>
     77<span style="font-family: FontFeaturesTestTTF;">BA</span>
     78<span style="font-family: FontFeaturesTestTTF;">BA</span>
     79<span style="font-family: FontFeaturesTestTTF;">BA</span>
     80<span style="font-family: FontFeaturesTestTTF;">BA</span>
     81<span style="font-family: FontFeaturesTestTTF;">BA</span>
     82<span style="font-family: FontFeaturesTestTTF;">BA</span>
     83</div>
    4684</body>
    4785</html>
  • trunk/LayoutTests/css3/font-feature-settings-rendering-2.html

    r190564 r190697  
    77    src: url("resources/FontWithFeatures.otf") format("opentype");
    88}
     9@font-face {
     10    font-family: "FontFeaturesTestTTF";
     11    src: url("resources/FontWithFeatures.ttf") format("truetype");
     12}
    913</style>
    1014</head>
     
    1317designed specifically for this purpose. The test passes if you see a sequence of alternating check
    1418marks and X below.
    15 <div id="insertionpoint"></div>
     19<div id="insertionPoint"></div>
     20<div id="insertionPoint2"></div>
    1621<script>
    17 var insertionpoint = document.getElementById("insertionpoint");
    18 function addElement(feature, c) {
     22var insertionPoint = document.getElementById("insertionPoint");
     23var insertionPoint2 = document.getElementById("insertionPoint2");
     24function addElement(placeToInsert, familyName, feature, c) {
    1925    ["0", "1"].map(function(state) {
    2026        var element = document.createElement("span");
    2127        element.textContent = c;
    22         element.style.fontFamily = "FontFeaturesTest";
     28        element.style.fontFamily = familyName;
    2329        element.style.fontFeatureSettings = '"' + feature + '" ' + state;
    24         insertionpoint.appendChild(element);
     30        placeToInsert.appendChild(element);
    2531    });
    26     insertionpoint.appendChild(document.createTextNode(" "));
     32    placeToInsert.appendChild(document.createTextNode(" "));
    2733}
    28 addElement("liga", "C");
    29 addElement("clig", "D");
    30 addElement("dlig", "E");
    31 addElement("hlig", "F");
    32 addElement("calt", "G");
    33 addElement("subs", "H");
    34 addElement("sups", "I");
    35 addElement("smcp", "J");
    36 addElement("c2sc", "K");
    37 addElement("pcap", "L");
    38 addElement("c2pc", "M");
    39 addElement("unic", "N");
    40 addElement("titl", "O");
    41 addElement("lnum", "P");
    42 addElement("onum", "Q");
    43 addElement("pnum", "R");
    44 addElement("tnum", "S");
    45 addElement("frac", "T");
    46 //addElement("afrc", "U");
    47 addElement("ordn", "V");
    48 addElement("zero", "W");
    49 addElement("hist", "X");
    50 addElement("jp78", "Y");
    51 addElement("jp83", "Z");
    52 addElement("jp90", "a");
    53 addElement("jp04", "b");
    54 addElement("smpl", "c");
    55 addElement("trad", "d");
    56 addElement("fwid", "e");
    57 addElement("pwid", "f");
    58 addElement("ruby", "g");
     34
     35addElement(insertionPoint, "FontFeaturesTest", "liga", "C");
     36addElement(insertionPoint, "FontFeaturesTest", "clig", "D");
     37addElement(insertionPoint, "FontFeaturesTest", "dlig", "E");
     38addElement(insertionPoint, "FontFeaturesTest", "hlig", "F");
     39addElement(insertionPoint, "FontFeaturesTest", "calt", "G");
     40addElement(insertionPoint, "FontFeaturesTest", "subs", "H");
     41addElement(insertionPoint, "FontFeaturesTest", "sups", "I");
     42addElement(insertionPoint, "FontFeaturesTest", "smcp", "J");
     43addElement(insertionPoint, "FontFeaturesTest", "c2sc", "K");
     44addElement(insertionPoint, "FontFeaturesTest", "pcap", "L");
     45addElement(insertionPoint, "FontFeaturesTest", "c2pc", "M");
     46addElement(insertionPoint, "FontFeaturesTest", "unic", "N");
     47addElement(insertionPoint, "FontFeaturesTest", "titl", "O");
     48addElement(insertionPoint, "FontFeaturesTest", "lnum", "P");
     49addElement(insertionPoint, "FontFeaturesTest", "onum", "Q");
     50addElement(insertionPoint, "FontFeaturesTest", "pnum", "R");
     51addElement(insertionPoint, "FontFeaturesTest", "tnum", "S");
     52addElement(insertionPoint, "FontFeaturesTest", "frac", "T");
     53//addElement(insertionPoint, "FontFeaturesTest", "afrc", "U");
     54addElement(insertionPoint, "FontFeaturesTest", "ordn", "V");
     55addElement(insertionPoint, "FontFeaturesTest", "zero", "W");
     56addElement(insertionPoint, "FontFeaturesTest", "hist", "X");
     57addElement(insertionPoint, "FontFeaturesTest", "jp78", "Y");
     58addElement(insertionPoint, "FontFeaturesTest", "jp83", "Z");
     59addElement(insertionPoint, "FontFeaturesTest", "jp90", "a");
     60addElement(insertionPoint, "FontFeaturesTest", "jp04", "b");
     61addElement(insertionPoint, "FontFeaturesTest", "smpl", "c");
     62addElement(insertionPoint, "FontFeaturesTest", "trad", "d");
     63addElement(insertionPoint, "FontFeaturesTest", "fwid", "e");
     64addElement(insertionPoint, "FontFeaturesTest", "pwid", "f");
     65addElement(insertionPoint, "FontFeaturesTest", "ruby", "g");
     66
     67addElement(insertionPoint2, "FontFeaturesTestTTF", "liga", "C");
     68addElement(insertionPoint2, "FontFeaturesTestTTF", "liga", "D");
     69addElement(insertionPoint2, "FontFeaturesTestTTF", "clig", "C");
     70addElement(insertionPoint2, "FontFeaturesTestTTF", "clig", "D");
     71addElement(insertionPoint2, "FontFeaturesTestTTF", "dlig", "G");
     72addElement(insertionPoint2, "FontFeaturesTestTTF", "hlig", "I");
     73addElement(insertionPoint2, "FontFeaturesTestTTF", "calt", "L");
     74addElement(insertionPoint2, "FontFeaturesTestTTF", "subs", "O");
     75addElement(insertionPoint2, "FontFeaturesTestTTF", "sups", "P");
     76addElement(insertionPoint2, "FontFeaturesTestTTF", "smcp", "S");
     77addElement(insertionPoint2, "FontFeaturesTestTTF", "c2sc", "V");
     78addElement(insertionPoint2, "FontFeaturesTestTTF", "pcap", "T");
     79addElement(insertionPoint2, "FontFeaturesTestTTF", "c2pc", "W");
     80addElement(insertionPoint2, "FontFeaturesTestTTF", "unic", "Y");
     81addElement(insertionPoint2, "FontFeaturesTestTTF", "titl", "a");
     82addElement(insertionPoint2, "FontFeaturesTestTTF", "lnum", "c");
     83addElement(insertionPoint2, "FontFeaturesTestTTF", "onum", "d");
     84addElement(insertionPoint2, "FontFeaturesTestTTF", "pnum", "f");
     85addElement(insertionPoint2, "FontFeaturesTestTTF", "tnum", "g");
     86addElement(insertionPoint2, "FontFeaturesTestTTF", "frac", "i");
     87addElement(insertionPoint2, "FontFeaturesTestTTF", "afrc", "j");
     88addElement(insertionPoint2, "FontFeaturesTestTTF", "ordn", "Q");
     89addElement(insertionPoint2, "FontFeaturesTestTTF", "zero", "k");
     90addElement(insertionPoint2, "FontFeaturesTestTTF", "hist", "K");
     91addElement(insertionPoint2, "FontFeaturesTestTTF", "jp78", "m");
     92addElement(insertionPoint2, "FontFeaturesTestTTF", "jp83", "n");
     93addElement(insertionPoint2, "FontFeaturesTestTTF", "jp90", "o");
     94addElement(insertionPoint2, "FontFeaturesTestTTF", "jp04", "p");
     95addElement(insertionPoint2, "FontFeaturesTestTTF", "smpl", "q");
     96addElement(insertionPoint2, "FontFeaturesTestTTF", "trad", "r");
     97addElement(insertionPoint2, "FontFeaturesTestTTF", "fwid", "t");
     98addElement(insertionPoint2, "FontFeaturesTestTTF", "pwid", "u");
     99addElement(insertionPoint2, "FontFeaturesTestTTF", "ruby", "v");
    59100</script>
    60101</body>
  • trunk/Tools/ChangeLog

    r190689 r190697  
     12015-10-07  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        Test font-variant-* and font-feature-settings with TrueType fonts
     4        https://bugs.webkit.org/show_bug.cgi?id=149776
     5
     6        Reviewed by Simon Fraser.
     7
     8        This test extends our existing FontWithFeatures project to be able to generate a
     9        TrueType font. This font is conceptually similar as the existing OpenType font,
     10        except the feature -> character mapping is different.
     11
     12        The font itself only supports the following characters:
     13        ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
     14        However, the shape of these letters are either an X or a check mark.
     15        The letter "A" always is a check mark.
     16        The letter "B" always is an X.
     17        Each font feature has an letter associated with it. When the font feature is enabled,
     18        that letter is shown as a check mark. For example, when
     19        "kLowerCaseType / kLowerCaseSmallCapsSelector" is enabled, "S" is shown as a check
     20        mark.
     21
     22        Here are the mappings of font features to letters:
     23        kLigaturesType / kCommonLigaturesOnSelector: C
     24        kLigaturesType / kContextualLigaturesOnSelector: D
     25        kLigaturesType / kRareLigaturesOnSelector: G
     26        kLigaturesType / kHistoricalLigaturesOnSelector: I
     27        kContextualAlternatesType / kContextualAlternatesOnSelector: L
     28        kVerticalPositionType / kInferiorsSelector: O
     29        kVerticalPositionType / kSuperiorsSelector: P
     30        kLowerCaseType / kLowerCaseSmallCapsSelector: S
     31        kUpperCaseType / kUpperCaseSmallCapsSelector: V
     32        kLowerCaseType / kLowerCasePetiteCapsSelector: T
     33        kUpperCaseType / kUpperCasePetiteCapsSelector: W
     34        kLetterCaseType / 14: Y
     35        kStyleOptionsType / kTitlingCapsSelector: a
     36        kNumberCaseType / kUpperCaseNumbersSelector: c
     37        kNumberCaseType / kLowerCaseNumbersSelector: d
     38        kNumberSpacingType / kProportionalNumbersSelector: f
     39        kNumberSpacingType / kMonospacedNumbersSelector: g
     40        kFractionsType / kDiagonalFractionsSelector: i
     41        kFractionsType / kVerticalFractionsSelector: j
     42        kVerticalPositionType / kOrdinalsSelector: Q
     43        kTypographicExtrasType / kSlashedZeroOnSelector: k
     44        kLigaturesType / kHistoricalLigaturesOnSelector: K
     45        kCharacterShapeType / kJIS1978CharactersSelector: m
     46        kCharacterShapeType / kJIS1983CharactersSelector: n
     47        kCharacterShapeType / kJIS1990CharactersSelector: o
     48        kCharacterShapeType / kJIS2004CharactersSelector: p
     49        kCharacterShapeType / kSimplifiedCharactersSelector: q
     50        kCharacterShapeType / kTraditionalCharactersSelector: r
     51        kTextSpacingType / kMonospacedTextSelector: t
     52        kTextSpacingType / kProportionalTextSelector: u
     53        kRubyKanaType / kRubyKanaOnSelector: v
     54
     55        * FontWithFeatures/FontWithFeatures.xcodeproj/project.pbxproj:
     56        * FontWithFeatures/FontWithFeatures/FontCreator.cpp:
     57        (CFFBuilder::moveTo):
     58        (CFFBuilder::lineTo):
     59        (GLYFBuilder::GLYFBuilder):
     60        (GLYFBuilder::takeResult):
     61        (GLYFBuilder::moveTo):
     62        (GLYFBuilder::lineTo):
     63        (GLYFBuilder::closePath):
     64        (GLYFBuilder::writePoint):
     65        (GLYFBuilder::append16):
     66        (generateBoxCharString):
     67        (generateCheckCharString):
     68        (generateXCharString):
     69        (itemForGlyph):
     70        (Generator::generate):
     71        (Generator::insertSelector):
     72        (Generator::insertFeature):
     73        (Generator::generateFeatureDescription):
     74        (Generator::appendCFFTable):
     75        (Generator::appendGLYFTable):
     76        (Generator::appendLOCATable):
     77        (Generator::appendFEATTable):
     78        (Generator::appendMetamorphosisChain):
     79        (Generator::appendMORXTable):
     80        (Generator::appendHEADTable):
     81        (Generator::appendHMTXTable):
     82        (Generator::appendNameSubtable):
     83        (Generator::append2ByteASCIIString):
     84        (Generator::appendNAMETable):
     85        (generateFont):
     86        (CFFBuilder::curveToCubic): Deleted.
     87        (charStringForGlyph): Deleted.
     88        * FontWithFeatures/FontWithFeatures/FontCreator.h:
     89        * FontWithFeatures/FontWithFeatures/main.cpp:
     90        (constructFontWithTrueTypeFeature):
     91        (constructFontWithOpenTypeFeature):
     92        (drawText):
     93        (main):
     94        (drawTextWithFeature): Deleted.
     95
    1962015-10-07  Commit Queue  <commit-queue@webkit.org>
    297
  • trunk/Tools/FontWithFeatures/FontWithFeatures.xcodeproj/project.pbxproj

    r189890 r190697  
    187187                                GCC_WARN_UNUSED_FUNCTION = YES;
    188188                                GCC_WARN_UNUSED_VARIABLE = YES;
    189                                 MACOSX_DEPLOYMENT_TARGET = 10.11;
     189                                MACOSX_DEPLOYMENT_TARGET = 10.10;
    190190                                MTL_ENABLE_DEBUG_INFO = YES;
    191191                                ONLY_ACTIVE_ARCH = YES;
     
    223223                                GCC_WARN_UNUSED_FUNCTION = YES;
    224224                                GCC_WARN_UNUSED_VARIABLE = YES;
    225                                 MACOSX_DEPLOYMENT_TARGET = 10.11;
     225                                MACOSX_DEPLOYMENT_TARGET = 10.10;
    226226                                MTL_ENABLE_DEBUG_INFO = NO;
    227227                                SDKROOT = macosx;
     
    262262                        );
    263263                        defaultConfigurationIsVisible = 0;
     264                        defaultConfigurationName = Release;
    264265                };
    265266/* End XCConfigurationList section */
  • trunk/Tools/FontWithFeatures/FontWithFeatures/FontCreator.cpp

    r190192 r190697  
    8383
    8484static const char rLineTo = 0x05;
    85 static const char rrCurveTo = 0x08;
    8685static const char endChar = 0x0e;
    8786static const char rMoveTo = 0x15;
     
    103102    }
    104103
    105     void moveTo(std::pair<float, float> targetPoint, bool closed)
    106     {
    107         if (closed && !result.empty())
    108             closePath();
    109 
    110         std::pair<float, float> destination = targetPoint;
    111 
    112         writePoint(destination);
     104    void moveTo(std::pair<float, float> targetPoint)
     105    {
     106        writePoint(targetPoint);
    113107        result.push_back(rMoveTo);
    114108
     
    118112    void lineTo(std::pair<float, float> targetPoint)
    119113    {
    120         std::pair<float, float> destination = targetPoint;
    121 
    122         writePoint(destination);
     114        writePoint(targetPoint);
    123115        result.push_back(rLineTo);
    124     }
    125 
    126     void curveToCubic(std::pair<float, float> point1, std::pair<float, float> point2, std::pair<float, float> targetPoint)
    127     {
    128         std::pair<float, float> destination1 = point1;
    129         std::pair<float, float> destination2 = point2;
    130         std::pair<float, float> destination3 = targetPoint;
    131 
    132         writePoint(destination1);
    133         writePoint(destination2);
    134         writePoint(destination3);
    135         result.push_back(rrCurveTo);
    136116    }
    137117
     
    157137};
    158138
     139class GLYFBuilder {
     140public:
     141    GLYFBuilder(float, std::pair<float, float>)
     142    {
     143    }
     144
     145    std::vector<uint8_t> takeResult()
     146    {
     147        std::vector<uint8_t> result;
     148        append16(result, endPtsOfContours.size());
     149        append16(result, clampTo<int16_t>(minX));
     150        append16(result, clampTo<int16_t>(minY));
     151        append16(result, clampTo<int16_t>(maxX));
     152        append16(result, clampTo<int16_t>(maxY));
     153
     154        for (uint16_t p : endPtsOfContours)
     155            append16(result, p);
     156        append16(result, 0);
     157        for (uint8_t f : flags)
     158            result.push_back(f);
     159        for (uint16_t c : xCoordinates)
     160            append16(result, c);
     161        for (uint16_t c : yCoordinates)
     162            append16(result, c);
     163
     164        return result;
     165    }
     166
     167    void moveTo(std::pair<float, float> targetPoint)
     168    {
     169        writePoint(targetPoint, true);
     170
     171        startingPoint = current;
     172    }
     173
     174    void lineTo(std::pair<float, float> targetPoint)
     175    {
     176        writePoint(targetPoint, true);
     177    }
     178
     179    void closePath()
     180    {
     181        if (current != startingPoint)
     182            lineTo(startingPoint);
     183        endPtsOfContours.push_back(pointCount - 1);
     184    }
     185
     186private:
     187    void writePoint(std::pair<float, float> destination, bool onCurve)
     188    {
     189        flags.push_back(onCurve ? 1 : 0); // Flags
     190
     191        std::pair<float, float> delta = std::make_pair(destination.first - current.first, destination.second - current.second);
     192        xCoordinates.push_back(delta.first);
     193        yCoordinates.push_back(delta.second);
     194
     195        current = destination;
     196        minX = std::min(minX, destination.first);
     197        maxX = std::max(maxX, destination.first);
     198        minY = std::min(minY, destination.second);
     199        maxY = std::max(maxY, destination.second);
     200        ++pointCount;
     201    }
     202
     203    static void append16(std::vector<uint8_t>& destination, uint16_t value)
     204    {
     205        destination.push_back(value >> 8);
     206        destination.push_back(value);
     207    }
     208
     209    std::vector<uint16_t> endPtsOfContours;
     210    std::vector<uint8_t> flags;
     211    std::vector<int16_t> xCoordinates;
     212    std::vector<int16_t> yCoordinates;
     213    std::pair<float, float> startingPoint;
     214    std::pair<float, float> current;
     215    float minX { std::numeric_limits<float>::max() };
     216    float maxX { std::numeric_limits<float>::min() };
     217    float minY { std::numeric_limits<float>::max() };
     218    float maxY { std::numeric_limits<float>::min() };
     219    unsigned pointCount { 0 };
     220};
     221
     222template <typename T>
    159223std::vector<uint8_t> generateBoxCharString()
    160224{
    161     CFFBuilder builder(unitsPerEm, std::make_pair(0.f, 0.f));
    162     builder.moveTo(std::make_pair(200.f, 200.f), false);
     225    T builder(unitsPerEm, std::make_pair(0.f, 0.f));
     226    builder.moveTo(std::make_pair(200.f, 200.f));
    163227    builder.lineTo(std::make_pair(200.f, 800.f));
    164228    builder.lineTo(std::make_pair(800.f, 800.f));
     
    168232}
    169233
     234template <typename T>
    170235std::vector<uint8_t> generateCheckCharString()
    171236{
    172     CFFBuilder builder(unitsPerEm, std::make_pair(0.f, 0.f));
    173     builder.moveTo(std::make_pair(200.f, 500.f), false);
     237    T builder(unitsPerEm, std::make_pair(0.f, 0.f));
     238    builder.moveTo(std::make_pair(200.f, 500.f));
    174239    builder.lineTo(std::make_pair(250.f, 550.f));
    175240    builder.lineTo(std::make_pair(500.f, 300.f));
     
    181246}
    182247
     248template <typename T>
    183249std::vector<uint8_t> generateXCharString()
    184250{
    185     CFFBuilder builder(unitsPerEm, std::make_pair(0.f, 0.f));
    186     builder.moveTo(std::make_pair(500.0f, 550.0f), false);
     251    T builder(unitsPerEm, std::make_pair(0.f, 0.f));
     252    builder.moveTo(std::make_pair(500.0f, 550.0f));
    187253    builder.lineTo(std::make_pair(900.f, 950.f));
    188254    builder.lineTo(std::make_pair(950.f, 900.f));
     
    200266}
    201267
    202 std::vector<uint8_t>& charStringForGlyph(uint16_t glyph, std::vector<uint8_t>& boxCharString, std::vector<uint8_t>& checkCharString, std::vector<uint8_t>& xCharString)
     268template<typename T>
     269const T& itemForGlyph(uint16_t glyph, const T& boxCharString, const T& checkCharString, const T& xCharString)
    203270{
    204271    if (!glyph)
     
    209276}
    210277
     278struct FeatureSelector {
     279    uint16_t selector;
     280    std::string name;
     281    uint16_t stringIndex;
     282    bool defaultSelector;
     283};
     284
     285struct FeatureType {
     286    uint16_t type;
     287    std::string name;
     288    uint16_t stringIndex;
     289    size_t settingTableOffsetLocation;
     290    std::vector<FeatureSelector> selectors;
     291    bool exclusive;
     292};
     293
    211294class Generator {
    212295public:
    213     std::vector<uint8_t> generate()
    214     {
    215         uint16_t numTables = 10;
     296    std::vector<uint8_t> generate(Type type)
     297    {
     298        featureDescription = generateFeatureDescription();
     299
     300        uint16_t numTables = type == Type::OpenType ? 10 : 12;
    216301        uint16_t roundedNumTables = roundDownToPowerOfTwo(numTables);
    217302        uint16_t searchRange = roundedNumTables * 16; // searchRange: "(Maximum power of 2 <= numTables) x 16."
    218303
    219         result.push_back('O');
    220         result.push_back('T');
    221         result.push_back('T');
    222         result.push_back('O');
     304        if (type == Type::OpenType) {
     305            result.push_back('O');
     306            result.push_back('T');
     307            result.push_back('T');
     308            result.push_back('O');
     309        } else {
     310            result.push_back('t');
     311            result.push_back('r');
     312            result.push_back('u');
     313            result.push_back('e');
     314        }
    223315        append16(numTables);
    224316        append16(searchRange);
     
    232324            result.push_back(0);
    233325
    234         appendTable("CFF ", &Generator::appendCFFTable);
    235         appendTable("GSUB", &Generator::appendGSUBTable);
     326        if (type == Type::OpenType) {
     327            appendTable("CFF ", &Generator::appendCFFTable);
     328            appendTable("GSUB", &Generator::appendGSUBTable);
     329        }
    236330        appendTable("OS/2", &Generator::appendOS2Table);
    237331        appendTable("cmap", &Generator::appendCMAPTable);
     332        if (type == Type::TrueType) {
     333            appendTable("feat", &Generator::appendFEATTable);
     334            appendTable("glyf", &Generator::appendGLYFTable);
     335        }
    238336        auto headTableOffset = result.size();
    239337        appendTable("head", &Generator::appendHEADTable);
    240338        appendTable("hhea", &Generator::appendHHEATable);
    241339        appendTable("hmtx", &Generator::appendHMTXTable);
     340        if (type == Type::TrueType)
     341            appendTable("loca", &Generator::appendLOCATable);
    242342        appendTable("maxp", &Generator::appendMAXPTable);
     343        if (type == Type::TrueType)
     344            appendTable("morx", &Generator::appendMORXTable);
    243345        appendTable("name", &Generator::appendNAMETable);
    244346        appendTable("post", &Generator::appendPOSTTable);
     
    249351        // sum the entire font as uint32, then store B1B0AFBA - sum. The checksum for the 'head' table will now be wrong. That is OK."
    250352        overwrite32(headTableOffset + 8, 0xB1B0AFBAU - calculateChecksum(0, result.size()));
     353
    251354        return std::move(result);
    252355    }
     
    331434        result[location + 2] = value >> 8;
    332435        result[location + 3] = value;
     436    }
     437
     438    void insertSelector(std::vector<FeatureSelector>& selectors, uint16_t selector, std::string selectorString, bool defaultSelector)
     439    {
     440        selectors.push_back({selector, selectorString, m_stringIndex++, defaultSelector});
     441    }
     442
     443    void insertFeature(std::vector<FeatureType>& result, uint16_t type, std::string typeString, uint16_t selector, std::string selectorString, bool exclusive)
     444    {
     445        // O(n) but performance is not an issue here
     446        for (size_t i = 0; i < result.size(); ++i) {
     447            if (result[i].type == type) {
     448                insertSelector(result[i].selectors, selector, selectorString, false);
     449                return;
     450            }
     451        }
     452        result.push_back({type, typeString, m_stringIndex++, 0, std::vector<FeatureSelector>(), exclusive});
     453        insertSelector(result[result.size() - 1].selectors, selector, selectorString, true);
     454    }
     455
     456    static const uint16_t kCharacterShapeType = 20;
     457    static const uint16_t kContextualAlternatesType = 36;
     458    static const uint16_t kFractionsType = 11;
     459    static const uint16_t kLetterCaseType = 3;
     460    static const uint16_t kLigaturesType = 1;
     461    static const uint16_t kLowerCaseType = 37;
     462    static const uint16_t kNumberCaseType = 21;
     463    static const uint16_t kNumberSpacingType = 6;
     464    static const uint16_t kRubyKanaType = 28;
     465    static const uint16_t kStyleOptionsType = 19;
     466    static const uint16_t kTextSpacingType = 22;
     467    static const uint16_t kTypographicExtrasType = 14;
     468    static const uint16_t kUpperCaseType = 38;
     469    static const uint16_t kVerticalPositionType = 10;
     470
     471    static const uint16_t kCommonLigaturesOffSelector = 3;
     472    static const uint16_t kCommonLigaturesOnSelector = 2;
     473    static const uint16_t kContextualAlternatesOffSelector = 1;
     474    static const uint16_t kContextualAlternatesOnSelector = 0;
     475    static const uint16_t kContextualLigaturesOffSelector = 19;
     476    static const uint16_t kContextualLigaturesOnSelector = 18;
     477    static const uint16_t kDiagonalFractionsSelector = 2;
     478    static const uint16_t kHistoricalLigaturesOffSelector = 21;
     479    static const uint16_t kHistoricalLigaturesOnSelector = 20;
     480    static const uint16_t kInferiorsSelector = 2;
     481    static const uint16_t kJIS1978CharactersSelector = 2;
     482    static const uint16_t kJIS1983CharactersSelector = 3;
     483    static const uint16_t kJIS1990CharactersSelector = 4;
     484    static const uint16_t kJIS2004CharactersSelector = 11;
     485    static const uint16_t kLowerCaseNumbersSelector = 0;
     486    static const uint16_t kLowerCasePetiteCapsSelector = 2;
     487    static const uint16_t kLowerCaseSmallCapsSelector = 1;
     488    static const uint16_t kMonospacedNumbersSelector = 0;
     489    static const uint16_t kMonospacedTextSelector = 1;
     490    static const uint16_t kOrdinalsSelector = 3;
     491    static const uint16_t kProportionalNumbersSelector = 1;
     492    static const uint16_t kProportionalTextSelector = 0;
     493    static const uint16_t kRareLigaturesOffSelector = 5;
     494    static const uint16_t kRareLigaturesOnSelector = 4;
     495    static const uint16_t kRubyKanaOnSelector = 2;
     496    static const uint16_t kRubyKanaSelector = 1;
     497    static const uint16_t kSimplifiedCharactersSelector = 1;
     498    static const uint16_t kSlashedZeroOnSelector = 4;
     499    static const uint16_t kSuperiorsSelector = 1;
     500    static const uint16_t kTitlingCapsSelector = 4;
     501    static const uint16_t kTraditionalCharactersSelector = 0;
     502    static const uint16_t kUpperCaseNumbersSelector = 1;
     503    static const uint16_t kUpperCasePetiteCapsSelector = 2;
     504    static const uint16_t kUpperCaseSmallCapsSelector = 1;
     505    static const uint16_t kVerticalFractionsSelector = 1;
     506
     507    static const uint16_t defaultUnusedSelector = 99;
     508
     509    std::vector<FeatureType> generateFeatureDescription()
     510    {
     511        std::vector<FeatureType> result;
     512
     513        // For any given feature type, the first selector inside it is the default selector for that type.
     514        insertFeature(result, kLigaturesType, "kLigaturesType", kCommonLigaturesOnSelector, "kCommonLigaturesOnSelector", false);
     515        insertFeature(result, kLigaturesType, "kLigaturesType", kContextualLigaturesOnSelector, "kContextualLigaturesOnSelector", false);
     516        insertFeature(result, kLigaturesType, "kLigaturesType", kCommonLigaturesOffSelector, "kCommonLigaturesOffSelector", false);
     517        insertFeature(result, kLigaturesType, "kLigaturesType", kContextualLigaturesOffSelector, "kContextualLigaturesOffSelector", false);
     518        insertFeature(result, kLigaturesType, "kLigaturesType", kRareLigaturesOnSelector, "kRareLigaturesOnSelector", false);
     519        insertFeature(result, kLigaturesType, "kLigaturesType", kRareLigaturesOffSelector, "kRareLigaturesOffSelector", false);
     520        insertFeature(result, kLigaturesType, "kLigaturesType", kHistoricalLigaturesOnSelector, "kHistoricalLigaturesOnSelector", false);
     521        insertFeature(result, kLigaturesType, "kLigaturesType", kHistoricalLigaturesOffSelector, "kHistoricalLigaturesOffSelector", false);
     522        insertFeature(result, kContextualAlternatesType, "kContextualAlternatesType", kContextualAlternatesOnSelector, "kContextualAlternatesOnSelector", false);
     523        insertFeature(result, kContextualAlternatesType, "kContextualAlternatesType", kContextualAlternatesOffSelector, "kContextualAlternatesOffSelector", false);
     524        insertFeature(result, kVerticalPositionType, "kVerticalPositionType", defaultUnusedSelector, "defaultUnusedSelector", true);
     525        insertFeature(result, kVerticalPositionType, "kVerticalPositionType", kInferiorsSelector, "kInferiorsSelector", true);
     526        insertFeature(result, kVerticalPositionType, "kVerticalPositionType", kSuperiorsSelector, "kSuperiorsSelector", true);
     527        insertFeature(result, kLowerCaseType, "kLowerCaseType", defaultUnusedSelector, "defaultUnusedSelector", true);
     528        insertFeature(result, kUpperCaseType, "kUpperCaseType", defaultUnusedSelector, "defaultUnusedSelector", true);
     529        insertFeature(result, kLowerCaseType, "kLowerCaseType", kLowerCaseSmallCapsSelector, "kLowerCaseSmallCapsSelector", true);
     530        insertFeature(result, kUpperCaseType, "kUpperCaseType", kUpperCaseSmallCapsSelector, "kUpperCaseSmallCapsSelector", true);
     531        insertFeature(result, kLowerCaseType, "kLowerCaseType", kLowerCasePetiteCapsSelector, "kLowerCasePetiteCapsSelector", true);
     532        insertFeature(result, kUpperCaseType, "kUpperCaseType", kUpperCasePetiteCapsSelector, "kUpperCasePetiteCapsSelector", true);
     533        insertFeature(result, kLetterCaseType, "kLetterCaseType", defaultUnusedSelector, "defaultUnusedSelector", true);
     534        insertFeature(result, kLetterCaseType, "kLetterCaseType", 14, "14", true);
     535        insertFeature(result, kStyleOptionsType, "kStyleOptionsType", defaultUnusedSelector, "defaultUnusedSelector", true);
     536        insertFeature(result, kStyleOptionsType, "kStyleOptionsType", kTitlingCapsSelector, "kTitlingCapsSelector", true);
     537        insertFeature(result, kNumberCaseType, "kNumberCaseType", defaultUnusedSelector, "defaultUnusedSelector", true);
     538        insertFeature(result, kNumberCaseType, "kNumberCaseType", kUpperCaseNumbersSelector, "kUpperCaseNumbersSelector", true);
     539        insertFeature(result, kNumberCaseType, "kNumberCaseType", kLowerCaseNumbersSelector, "kLowerCaseNumbersSelector", true);
     540        insertFeature(result, kNumberSpacingType, "kNumberSpacingType", defaultUnusedSelector, "defaultUnusedSelector", true);
     541        insertFeature(result, kNumberSpacingType, "kNumberSpacingType", kProportionalNumbersSelector, "kProportionalNumbersSelector", true);
     542        insertFeature(result, kNumberSpacingType, "kNumberSpacingType", kMonospacedNumbersSelector, "kMonospacedNumbersSelector", true);
     543        insertFeature(result, kFractionsType, "kFractionsType", defaultUnusedSelector, "defaultUnusedSelector", true);
     544        insertFeature(result, kFractionsType, "kFractionsType", kDiagonalFractionsSelector, "kDiagonalFractionsSelector", true);
     545        insertFeature(result, kFractionsType, "kFractionsType", kVerticalFractionsSelector, "kVerticalFractionsSelector", true);
     546        insertFeature(result, kVerticalPositionType, "kVerticalPositionType", kOrdinalsSelector, "kOrdinalsSelector", true);
     547        insertFeature(result, kTypographicExtrasType, "kTypographicExtrasType", kSlashedZeroOnSelector, "kSlashedZeroOnSelector", false);
     548        insertFeature(result, kLigaturesType, "kLigaturesType", kHistoricalLigaturesOnSelector, "kHistoricalLigaturesOnSelector", false);
     549        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", defaultUnusedSelector, "defaultUnusedSelector", true);
     550        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kJIS1978CharactersSelector, "kJIS1978CharactersSelector", true);
     551        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kJIS1983CharactersSelector, "kJIS1983CharactersSelector", true);
     552        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kJIS1990CharactersSelector, "kJIS1990CharactersSelector", true);
     553        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kJIS2004CharactersSelector, "kJIS2004CharactersSelector", true);
     554        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kSimplifiedCharactersSelector, "kSimplifiedCharactersSelector", true);
     555        insertFeature(result, kCharacterShapeType, "kCharacterShapeType", kTraditionalCharactersSelector, "kTraditionalCharactersSelector", true);
     556        insertFeature(result, kTextSpacingType, "kTextSpacingType", defaultUnusedSelector, "defaultUnusedSelector", true);
     557        insertFeature(result, kTextSpacingType, "kTextSpacingType", kMonospacedTextSelector, "kMonospacedTextSelector", true);
     558        insertFeature(result, kTextSpacingType, "kTextSpacingType", kProportionalTextSelector, "kProportionalTextSelector", true);
     559        insertFeature(result, kRubyKanaType, "kRubyKanaType", kRubyKanaOnSelector, "kRubyKanaOnSelector", false);
     560
     561        return result;
    333562    }
    334563   
     
    419648
    420649        // CharStrings INDEX
    421         std::vector<uint8_t> boxCharString = generateBoxCharString();
    422         std::vector<uint8_t> checkCharString = generateCheckCharString();
    423         std::vector<uint8_t> xCharString = generateXCharString();
     650        std::vector<uint8_t> boxCharString = generateBoxCharString<CFFBuilder>();
     651        std::vector<uint8_t> checkCharString = generateCheckCharString<CFFBuilder>();
     652        std::vector<uint8_t> xCharString = generateXCharString<CFFBuilder>();
    424653        assert(numGlyphs > 26);
    425654        overwrite32(charstringsOffsetLocation, static_cast<uint32_t>(result.size() - startingOffset));
     
    429658        append32(offset);
    430659        for (uint16_t glyph = 0; glyph < numGlyphs; ++glyph) {
    431             offset += charStringForGlyph(glyph, boxCharString, checkCharString, xCharString).size();
     660            offset += itemForGlyph(glyph, boxCharString, checkCharString, xCharString).size();
    432661            append32(offset);
    433662        }
    434663        for (uint16_t glyph = 0; glyph < numGlyphs; ++glyph) {
    435             std::vector<uint8_t>& charString = charStringForGlyph(glyph, boxCharString, checkCharString, xCharString);
     664            const std::vector<uint8_t>& charString = itemForGlyph(glyph, boxCharString, checkCharString, xCharString);
    436665            result.insert(result.end(), charString.begin(), charString.end());
    437666        }
     667    }
     668
     669    // Keep in sync with loca
     670    void appendGLYFTable()
     671    {
     672        std::vector<uint8_t> boxCharString = generateBoxCharString<GLYFBuilder>();
     673        std::vector<uint8_t> checkCharString = generateCheckCharString<GLYFBuilder>();
     674        std::vector<uint8_t> xCharString = generateXCharString<GLYFBuilder>();
     675        for (uint16_t glyph = 0; glyph < numGlyphs; ++glyph) {
     676            const std::vector<uint8_t>& charString = itemForGlyph(glyph, boxCharString, checkCharString, xCharString);
     677            result.insert(result.end(), charString.begin(), charString.end());
     678        }
     679    }
     680
     681    // Keep in sync with glyf
     682    void appendLOCATable()
     683    {
     684        std::vector<uint8_t> boxCharString = generateBoxCharString<GLYFBuilder>();
     685        std::vector<uint8_t> checkCharString = generateCheckCharString<GLYFBuilder>();
     686        std::vector<uint8_t> xCharString = generateXCharString<GLYFBuilder>();
     687        uint32_t index = 0;
     688        for (uint16_t glyph = 0; glyph < numGlyphs; ++glyph) {
     689            append32(index);
     690            index += itemForGlyph(glyph, boxCharString, checkCharString, xCharString).size();
     691        }
     692        append32(index);
     693    }
     694
     695    void appendFEATTable()
     696    {
     697        size_t tableLocation = result.size();
     698        append32(0x00010000); // Version
     699        append16(featureDescription.size()); // Number of entries in the feature name array
     700        append16(0); // reserved
     701        append32(0); // reserved
     702
     703        // Feature name array
     704        for (FeatureType& type : featureDescription) {
     705            append16(type.type); // Feature type
     706            append16(type.selectors.size()); // Number of settings
     707            type.settingTableOffsetLocation = result.size();
     708            append32(0); // Offset in bytes from beginning of this table to feature's setting name array
     709            append16(type.exclusive ? 0x8000 : 0); // Flags. 0x8000 = Exclusive
     710            append16(type.stringIndex + m_baseStringIndex); // Index in the name table for the name of this feature
     711        }
     712
     713        // Setting name array
     714        for (FeatureType& type : featureDescription) {
     715            overwrite32(type.settingTableOffsetLocation, static_cast<uint32_t>(result.size() - tableLocation));
     716            for (FeatureSelector& selector : type.selectors) {
     717                append16(selector.selector); // Setting: kNormalPositionSelector (initial setting is default)
     718                append16(selector.stringIndex + m_baseStringIndex); // Index in the name table for the name of this setting
     719            }
     720        }
     721    }
     722
     723    void appendMetamorphosisChain(const FeatureType& type, const FeatureSelector& selector, uint16_t glyphToReplace, uint16_t withMe)
     724    {
     725        size_t chainLocation = result.size();
     726        append32(type.exclusive && selector.defaultSelector ? 1 : 0); // Default flags
     727        size_t chainSizeLocation = result.size();
     728        append32(0); // Placeholder for chain length in bytes (padded to multiple of 4)
     729        append32(2); // Number of feature subtable entries
     730        append32(1); // Number of subtables in the chain
     731
     732        // Feature table
     733        append16(type.type); // Feature type
     734        append16(selector.selector); // Feature selector
     735        append32(1); // Enable flags
     736        append32(0xFFFFFFFF); // disable flags
     737
     738        // Feature table 2
     739        append16(0); // Feature type: kAllTypographicFeaturesType
     740        append16(1); // Feature selector: kAllTypeFeaturesOffSelector
     741        append32(0); // Enable flags
     742        append32(0); // disable flags
     743
     744        // Metamorphosis subtable
     745        size_t metamorphosisSubtableSizeLocation = result.size();
     746        append32(0); // Placeholder for chain length in bytes (padded to multiple of 4)
     747        append32(4); // Coverage flags and subtable type. Subtable type 4: Noncontextual ("swash") subtable
     748        append32(1); // subFeature flags
     749
     750        // Non-contextual glyph substitution subtable
     751        append16(6); // Lookup format: sorted list of (glyph index, lookup value) pairs
     752       
     753        // BinSrchHeader
     754        append16(4); // Size of a lookup unit for this search in bytes
     755        append16(1); // Number of units to be searched
     756        append16(4); // Search range: The value of unitSize times the largest power of 2 that is less than or equal to the value of nUnits.
     757        append16(0); // Entry selector: The log base 2 of the largest power of 2 less than or equal to the value of nUnits.
     758        append16(0); // Range shift: The value of unitSize times the difference of the value of nUnits minus the largest power of 2 less than or equal to the value of nUnits.
     759        // Entries
     760        append16(glyphToReplace);
     761        append16(withMe);
     762
     763        overwrite32(metamorphosisSubtableSizeLocation, static_cast<uint32_t>(result.size() - metamorphosisSubtableSizeLocation));
     764
     765        while (result.size() % 4)
     766            result.push_back(0);
     767        overwrite32(chainSizeLocation, static_cast<uint32_t>(result.size() - chainLocation));
     768    }
     769
     770    void appendMORXTable()
     771    {
     772        append16(2); // Version
     773        append16(0); // Unused
     774        size_t numberOfChainsLocation = result.size();
     775        append32(0); // Number of metamorphosis chains placeholder
     776
     777        int count = 0;
     778        for (FeatureType& type : featureDescription) {
     779            for (FeatureSelector& selector : type.selectors) {
     780                appendMetamorphosisChain(type, selector, count + 3, 1);
     781                count++;
     782            }
     783        }
     784   
     785        overwrite32(numberOfChainsLocation, count);
    438786    }
    439787
     
    6781026        append16(3); // Smallest readable size in pixels
    6791027        append16(0); // Might contain LTR or RTL glyphs
    680         append16(0); // Short offsets in the 'loca' table. However, OTF fonts don't have a 'loca' table so this is irrelevant
     1028        append16(1); // Long offsets in the 'loca' table.
    6811029        append16(0); // Glyph data format
    6821030    }
     
    7061054    {
    7071055        for (unsigned i = 0; i < numGlyphs; ++i) {
    708             append16(clampTo<uint16_t>(unitsPerEm)); // horizontal advance
    709             append16(clampTo<int16_t>(0)); // left side bearing
     1056            append16(clampTo<uint16_t>(static_cast<int32_t>(unitsPerEm))); // horizontal advance
     1057            append16(itemForGlyph(i, 200, 200, 50)); // left side bearing
    7101058        }
    7111059    }
     
    7291077        append16(0); // No compound glyphs
    7301078    }
    731    
    732     void appendNAMETable()
    733     {
    734         std::string fontName = "MylesFont";
    735 
    736         append16(0); // Format selector
    737         append16(1); // Number of name records in table
    738         append16(18); // Offset in bytes to the beginning of name character strings
    739 
     1079
     1080    void appendNameSubtable(const std::string& s, uint16_t nameIdentifier)
     1081    {
    7401082        append16(0); // Unicode
    7411083        append16(3); // Unicode version 2.0 or later
    7421084        append16(0); // Language
    743         append16(1); // Name identifier. 1 = Font family
    744         append16(fontName.length());
    745         append16(0); // Offset into name data
    746 
    747         for (auto codeUnit : fontName)
     1085        append16(m_baseStringIndex + nameIdentifier); // Name identifier
     1086        append16(s.length());
     1087        append16(m_nameOffset); // Offset into name data
     1088        m_nameOffset += s.size() * 2; // Code units get 2 bytes each
     1089    }
     1090
     1091    void append2ByteASCIIString(std::string& s)
     1092    {
     1093        for (auto codeUnit : s)
    7481094            append16(codeUnit);
     1095    }
     1096
     1097    void appendNAMETable()
     1098    {
     1099        std::string familyName = "MylesFont"; // 1: Font Family
     1100
     1101        uint16_t numberOfRecords = m_stringIndex + 1;
     1102        append16(0); // Format selector
     1103        append16(numberOfRecords); // Number of name records in table
     1104        append16(6 + 12 * numberOfRecords); // Offset in bytes to the beginning of name character strings
     1105
     1106        appendNameSubtable(familyName, 1); // 1: Font Family
     1107        for (FeatureType& type : featureDescription) {
     1108            appendNameSubtable(type.name, type.stringIndex);
     1109            for (FeatureSelector& selector : type.selectors)
     1110                appendNameSubtable(selector.name, selector.stringIndex);
     1111        }
     1112
     1113        append2ByteASCIIString(familyName);
     1114        for (FeatureType& type : featureDescription) {
     1115            append2ByteASCIIString(type.name);
     1116            for (FeatureSelector& selector : type.selectors)
     1117                append2ByteASCIIString(selector.name);
     1118        }
    7491119    }
    7501120   
     
    7971167
    7981168    unsigned m_tablesAppendedCount { 0 };
     1169    unsigned m_nameOffset { 0 };
     1170    static constexpr uint16_t m_baseStringIndex { 257 };
     1171    uint16_t m_stringIndex { 0 };
     1172    std::vector<FeatureType> featureDescription;
    7991173    std::vector<uint8_t> result;
    8001174};
    8011175
    802 std::vector<uint8_t> generateFont()
     1176std::vector<uint8_t> generateFont(Type type)
    8031177{
    804     return Generator().generate();
     1178    return Generator().generate(type);
    8051179}
  • trunk/Tools/FontWithFeatures/FontWithFeatures/FontCreator.h

    r189943 r190697  
    2929#include <vector>
    3030
    31 std::vector<uint8_t> generateFont();
     31enum class Type {
     32    OpenType,
     33    TrueType
     34};
     35
     36std::vector<uint8_t> generateFont(Type);
    3237
    3338#endif /* FontCreator_h */
  • trunk/Tools/FontWithFeatures/FontWithFeatures/main.cpp

    r189943 r190697  
    3333#include <fstream>
    3434
    35 void drawTextWithFeature(CGContextRef context, CTFontDescriptorRef fontDescriptor, CFStringRef feature, int value, CGPoint location)
    36 {
    37     CGFloat fontSize = 25;
    38     CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, 1));
    39     CGContextSetTextPosition(context, location.x, location.y);
    40 
     35static CTFontDescriptorRef constructFontWithTrueTypeFeature(CTFontDescriptorRef fontDescriptor, int type, int selector)
     36{
     37    CFNumberRef typeValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type);
     38    CFNumberRef selectorValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &selector);
     39    CFTypeRef featureDictionaryKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
     40    CFTypeRef featureDictionaryValues[] = { typeValue, selectorValue };
     41    CFDictionaryRef featureDictionary = CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     42    CFRelease(typeValue);
     43    CFRelease(selectorValue);
     44
     45    CFTypeRef featureSettingsValues[] = { featureDictionary };
     46    CFArrayRef fontFeatureSettings = CFArrayCreate(kCFAllocatorDefault, featureSettingsValues, 1, &kCFTypeArrayCallBacks);
     47    CFRelease(featureDictionary);
     48
     49    CFTypeRef fontDescriptorKeys[] = { kCTFontFeatureSettingsAttribute };
     50    CFTypeRef fontDescriptorValues[] = { fontFeatureSettings };
     51    CFDictionaryRef fontDescriptorAttributes = CFDictionaryCreate(kCFAllocatorDefault, fontDescriptorKeys, fontDescriptorValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     52    CFRelease(fontFeatureSettings);
     53
     54    CTFontDescriptorRef modifiedFontDescriptor = CTFontDescriptorCreateCopyWithAttributes(fontDescriptor, fontDescriptorAttributes);
     55    CFRelease(fontDescriptorAttributes);
     56    return modifiedFontDescriptor;
     57}
     58
     59static CTFontDescriptorRef constructFontWithOpenTypeFeature(CTFontDescriptorRef fontDescriptor, CFStringRef feature, int value)
     60{
    4161    CFNumberRef featureValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
    4262    CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
     
    5676    CTFontDescriptorRef modifiedFontDescriptor = CTFontDescriptorCreateCopyWithAttributes(fontDescriptor, fontDescriptorAttributes);
    5777    CFRelease(fontDescriptorAttributes);
    58 
    59     CTFontRef font = CTFontCreateWithFontDescriptor(modifiedFontDescriptor, fontSize, nullptr);
    60     CFRelease(modifiedFontDescriptor);
     78    return modifiedFontDescriptor;
     79}
     80
     81static void drawText(CGContextRef context, CTFontDescriptorRef fontDescriptor, CFStringRef prefix, CGPoint location)
     82{
     83    CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, 1));
     84    CGContextSetTextPosition(context, location.x, location.y);
     85
     86    CGFloat fontSize = 25;
     87    CTFontRef font = CTFontCreateWithFontDescriptor(fontDescriptor, fontSize, nullptr);
    6188
    6289    CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0);
    63     CFStringAppend(string, feature);
    64     CFStringAppend(string, value ? CFSTR("  (on)") : CFSTR(" (off)"));
    65     CFStringAppend(string, CFSTR(": ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
     90    CFStringAppend(string, prefix);
     91    CFStringAppend(string, CFSTR("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"));
    6692
    6793    CGColorRef red = CGColorCreateGenericRGB(1, 0, 0, 1);
     
    7399    CFAttributedStringRef attributedString = CFAttributedStringCreate(kCFAllocatorDefault, string, lineAttributes);
    74100    CFRelease(lineAttributes);
    75     CFRelease(string);
    76101
    77102    CFMutableAttributedStringRef mutableAttributedString = CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, 0, attributedString);
     
    79104
    80105    CTFontRef monospaceFont = CTFontCreateWithName(CFSTR("Courier"), fontSize, nullptr);
    81     CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(0, 12), kCTFontAttributeName, monospaceFont);
     106    CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(0, CFStringGetLength(prefix)), kCTFontAttributeName, monospaceFont);
    82107    CFRelease(monospaceFont);
    83108
    84     CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(12, 52), kCTFontAttributeName, font);
     109    CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(CFStringGetLength(prefix), CFStringGetLength(string) - CFStringGetLength(prefix)), kCTFontAttributeName, font);
     110    CFRelease(string);
    85111    CFRelease(font);
    86112
     
    94120int main(int argc, const char * argv[])
    95121{
    96     size_t width = 2000;
     122    size_t width = 2500;
    97123    size_t height = 2000;
    98124    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    99125    CGContextRef context = CGBitmapContextCreate(nullptr, width, height, 8, width * 4, colorSpace, kCGImageAlphaNoneSkipLast);
    100126    CGColorSpaceRelease(colorSpace);
    101     const std::vector<uint8_t> fontVector = generateFont();
    102     std::ofstream outputFile("/Volumes/Data/home/mmaxfield/tmp/output.otf", std::ios::out | std::ios::binary);
     127    Type type = Type::TrueType;
     128    const std::vector<uint8_t> fontVector = generateFont(type);
     129    std::ofstream outputFile("/Volumes/Data/home/mmaxfield/tmp/output.ttf", std::ios::out | std::ios::binary);
    103130    for (uint8_t b : fontVector)
    104131        outputFile << b;
     
    109136    CFRelease(fontData);
    110137
    111     CFTypeRef featureValues[] = { CFSTR("liga"), CFSTR("clig"), CFSTR("dlig"), CFSTR("hlig"), CFSTR("calt"), CFSTR("subs"), CFSTR("sups"), CFSTR("smcp"), CFSTR("c2sc"), CFSTR("pcap"), CFSTR("c2pc"), CFSTR("unic"), CFSTR("titl"), CFSTR("onum"), CFSTR("pnum"), CFSTR("tnum"), CFSTR("frac"), CFSTR("afrc"), CFSTR("ordn"), CFSTR("zero"), CFSTR("hist"), CFSTR("jp78"), CFSTR("jp83"), CFSTR("jp90"), CFSTR("jp04"), CFSTR("smpl"), CFSTR("trad"), CFSTR("fwid"), CFSTR("pwid"), CFSTR("ruby") };
    112     CFArrayRef features = CFArrayCreate(kCFAllocatorDefault, featureValues, 30, &kCFTypeArrayCallBacks);
    113 
    114     for (CFIndex i = 0; i < CFArrayGetCount(features); ++i) {
    115         drawTextWithFeature(context, fontDescriptor, static_cast<CFStringRef>(CFArrayGetValueAtIndex(features, i)), 1, CGPointMake(25, 1950 - 50 * i));
    116         drawTextWithFeature(context, fontDescriptor, static_cast<CFStringRef>(CFArrayGetValueAtIndex(features, i)), 0, CGPointMake(25, 1925 - 50 * i));
     138    if (type == Type::OpenType) {
     139        CFTypeRef featureValuesOpenType[] = { CFSTR("liga"), CFSTR("clig"), CFSTR("dlig"), CFSTR("hlig"), CFSTR("calt"), CFSTR("subs"), CFSTR("sups"), CFSTR("smcp"), CFSTR("c2sc"), CFSTR("pcap"), CFSTR("c2pc"), CFSTR("unic"), CFSTR("titl"), CFSTR("onum"), CFSTR("pnum"), CFSTR("tnum"), CFSTR("frac"), CFSTR("afrc"), CFSTR("ordn"), CFSTR("zero"), CFSTR("hist"), CFSTR("jp78"), CFSTR("jp83"), CFSTR("jp90"), CFSTR("jp04"), CFSTR("smpl"), CFSTR("trad"), CFSTR("fwid"), CFSTR("pwid"), CFSTR("ruby") };
     140        CFArrayRef features = CFArrayCreate(kCFAllocatorDefault, featureValuesOpenType, 30, &kCFTypeArrayCallBacks);
     141
     142        for (CFIndex i = 0; i < CFArrayGetCount(features); ++i) {
     143            CFStringRef feature = static_cast<CFStringRef>(CFArrayGetValueAtIndex(features, i));
     144            CTFontDescriptorRef modifiedFontDescriptor = constructFontWithOpenTypeFeature(fontDescriptor, feature, 1);
     145            CFMutableStringRef prefix = CFStringCreateMutable(kCFAllocatorDefault, 0);
     146            CFStringAppend(prefix, feature);
     147            CFStringAppend(prefix, CFSTR("  (on): "));
     148            drawText(context, modifiedFontDescriptor, prefix, CGPointMake(25, 1950 - 50 * i));
     149            CFRelease(prefix);
     150            CFRelease(modifiedFontDescriptor);
     151
     152            modifiedFontDescriptor = constructFontWithOpenTypeFeature(fontDescriptor, feature, 0);
     153            prefix = CFStringCreateMutable(kCFAllocatorDefault, 0);
     154            CFStringAppend(prefix, feature);
     155            CFStringAppend(prefix, CFSTR(" (off): "));
     156            drawText(context, modifiedFontDescriptor, prefix, CGPointMake(25, 1925 - 50 * i));
     157            CFRelease(prefix);
     158            CFRelease(modifiedFontDescriptor);
     159        }
     160
     161        CFRelease(features);
     162    } else {
     163        __block int i = 0;
     164        void (^handler)(uint16_t type, CFStringRef typeString, uint16_t selector, CFStringRef selectorString) = ^(uint16_t type, CFStringRef typeString, uint16_t selector, CFStringRef selectorString)
     165        {
     166            CTFontDescriptorRef modifiedFontDescriptor = constructFontWithTrueTypeFeature(fontDescriptor, type, selector);
     167            CFMutableStringRef prefix = CFStringCreateMutable(kCFAllocatorDefault, 0);
     168            CFStringAppend(prefix, typeString);
     169            CFStringAppend(prefix, CFSTR(": "));
     170            CFStringAppend(prefix, selectorString);
     171            CFStringAppend(prefix, CFSTR(": "));
     172            while (CFStringGetLength(prefix) < 65)
     173                CFStringAppend(prefix, CFSTR(" "));
     174            drawText(context, modifiedFontDescriptor, prefix, CGPointMake(25, 1950 - 40 * i));
     175            CFRelease(prefix);
     176            CFRelease(modifiedFontDescriptor);
     177            ++i;
     178        };
     179        handler(kLigaturesType, CFSTR("kLigaturesType"), kCommonLigaturesOnSelector, CFSTR("kCommonLigaturesOnSelector"));
     180        handler(kLigaturesType, CFSTR("kLigaturesType"), kContextualLigaturesOnSelector, CFSTR("kContextualLigaturesOnSelector"));
     181        handler(kLigaturesType, CFSTR("kLigaturesType"), kCommonLigaturesOffSelector, CFSTR("kCommonLigaturesOffSelector"));
     182        handler(kLigaturesType, CFSTR("kLigaturesType"), kContextualLigaturesOffSelector, CFSTR("kContextualLigaturesOffSelector"));
     183        handler(kLigaturesType, CFSTR("kLigaturesType"), kRareLigaturesOnSelector, CFSTR("kRareLigaturesOnSelector"));
     184        handler(kLigaturesType, CFSTR("kLigaturesType"), kRareLigaturesOffSelector, CFSTR("kRareLigaturesOffSelector"));
     185        handler(kLigaturesType, CFSTR("kLigaturesType"), kHistoricalLigaturesOnSelector, CFSTR("kHistoricalLigaturesOnSelector"));
     186        handler(kLigaturesType, CFSTR("kLigaturesType"), kHistoricalLigaturesOffSelector, CFSTR("kHistoricalLigaturesOffSelector"));
     187        handler(kContextualAlternatesType, CFSTR("kContextualAlternatesType"), kContextualAlternatesOnSelector, CFSTR("kContextualAlternatesOnSelector"));
     188        handler(kContextualAlternatesType, CFSTR("kContextualAlternatesType"), kContextualAlternatesOffSelector, CFSTR("kContextualAlternatesOffSelector"));
     189        handler(kVerticalPositionType, CFSTR("kVerticalPositionType"), kInferiorsSelector, CFSTR("kInferiorsSelector"));
     190        handler(kVerticalPositionType, CFSTR("kVerticalPositionType"), kSuperiorsSelector, CFSTR("kSuperiorsSelector"));
     191        handler(kLowerCaseType, CFSTR("kLowerCaseType"), kLowerCaseSmallCapsSelector, CFSTR("kLowerCaseSmallCapsSelector"));
     192        handler(kUpperCaseType, CFSTR("kUpperCaseType"), kUpperCaseSmallCapsSelector, CFSTR("kUpperCaseSmallCapsSelector"));
     193        handler(kLowerCaseType, CFSTR("kLowerCaseType"), kLowerCasePetiteCapsSelector, CFSTR("kLowerCasePetiteCapsSelector"));
     194        handler(kUpperCaseType, CFSTR("kUpperCaseType"), kUpperCasePetiteCapsSelector, CFSTR("kUpperCasePetiteCapsSelector"));
     195        handler(kLetterCaseType, CFSTR("kLetterCaseType"), 14, CFSTR("14"));
     196        handler(kStyleOptionsType, CFSTR("kStyleOptionsType"), kTitlingCapsSelector, CFSTR("kTitlingCapsSelector"));
     197        handler(kNumberCaseType, CFSTR("kNumberCaseType"), kUpperCaseNumbersSelector, CFSTR("kUpperCaseNumbersSelector"));
     198        handler(kNumberCaseType, CFSTR("kNumberCaseType"), kLowerCaseNumbersSelector, CFSTR("kLowerCaseNumbersSelector"));
     199        handler(kNumberSpacingType, CFSTR("kNumberSpacingType"), kProportionalNumbersSelector, CFSTR("kProportionalNumbersSelector"));
     200        handler(kNumberSpacingType, CFSTR("kNumberSpacingType"), kMonospacedNumbersSelector, CFSTR("kMonospacedNumbersSelector"));
     201        handler(kFractionsType, CFSTR("kFractionsType"), kDiagonalFractionsSelector, CFSTR("kDiagonalFractionsSelector"));
     202        handler(kFractionsType, CFSTR("kFractionsType"), kVerticalFractionsSelector, CFSTR("kVerticalFractionsSelector"));
     203        handler(kVerticalPositionType, CFSTR("kVerticalPositionType"), kOrdinalsSelector, CFSTR("kOrdinalsSelector"));
     204        handler(kTypographicExtrasType, CFSTR("kTypographicExtrasType"), kSlashedZeroOnSelector, CFSTR("kSlashedZeroOnSelector"));
     205        handler(kLigaturesType, CFSTR("kLigaturesType"), kHistoricalLigaturesOnSelector, CFSTR("kHistoricalLigaturesOnSelector"));
     206        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kJIS1978CharactersSelector, CFSTR("kJIS1978CharactersSelector"));
     207        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kJIS1983CharactersSelector, CFSTR("kJIS1983CharactersSelector"));
     208        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kJIS1990CharactersSelector, CFSTR("kJIS1990CharactersSelector"));
     209        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kJIS2004CharactersSelector, CFSTR("kJIS2004CharactersSelector"));
     210        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kSimplifiedCharactersSelector, CFSTR("kSimplifiedCharactersSelector"));
     211        handler(kCharacterShapeType, CFSTR("kCharacterShapeType"), kTraditionalCharactersSelector, CFSTR("kTraditionalCharactersSelector"));
     212        handler(kTextSpacingType, CFSTR("kTextSpacingType"), kMonospacedTextSelector, CFSTR("kMonospacedTextSelector"));
     213        handler(kTextSpacingType, CFSTR("kTextSpacingType"), kProportionalTextSelector, CFSTR("kProportionalTextSelector"));
     214        handler(kRubyKanaType, CFSTR("kRubyKanaType"), kRubyKanaOnSelector, CFSTR("kRubyKanaOnSelector"));
    117215    }
    118216
    119     CFRelease(features);
    120217    CFRelease(fontDescriptor);
    121218    CGImageRef image = CGBitmapContextCreateImage(context);
Note: See TracChangeset for help on using the changeset viewer.