font-variant-caps does not work if the font does not support font features
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Dec 2015 08:37:03 +0000 (08:37 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Dec 2015 08:37:03 +0000 (08:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149774

Reviewed by Antti Koivisto.

Source/WebCore:

This test implements synthesis for small-caps and all-small-caps. It does so by
moving font variant selection into a higher level (ComplexTextController).
In general, the approach is to use the pure font feature until we encounter
a character which needs to be uppercased, and which the font feature does not
support uppercasing. In this situation, we try again with synthesis. In this
case, synthesis means artificially uppercasing letters and rendering them with
a smaller font.

We require system support to know which glyphs a particular font feature supports.
Therefore, on operating systems which do not include this support, we will simply
say that the font feature does not support any glyphs.

Test: css3/font-variant-small-caps-synthesis.html
      css3/font-variant-petite-caps-synthesis.html

* platform/graphics/Font.cpp:
(WebCore::Font::noSmallCapsFont): Return the same font, but without smcp or c2sc.
This function utilizes a cache.
* platform/graphics/Font.h:
(WebCore::Font::variantFont): Small caps should never go through this function
anymore.
* platform/graphics/FontCascade.h: Because we're moving variant selection into
a higher level, we remove the FontVariant argument from the lower-level call.
* platform/graphics/FontCascadeFonts.cpp:
(WebCore::FontCascadeFonts::glyphDataForVariant): Use early-return style.
(WebCore::FontCascadeFonts::glyphDataForNormalVariant): Ditto.
* platform/graphics/cocoa/FontCascadeCocoa.mm:
(WebCore::FontCascade::fontForCombiningCharacterSequence): Because we're moving
variant selection into a higher level, we remove the FontVariant argument from
the lower-level call.
* platform/graphics/cocoa/FontCocoa.mm:
(WebCore::Font::smallCapsSupportsCharacter):
(WebCore::Font::allSmallCapsSupportsCharacter):
(WebCore::smallCapsOpenTypeDictionary): Helper function for
smallCapsSupportsCharacter().
(WebCore::smallCapsTrueTypeDictionary): Ditto.
(WebCore::unionBitVectors):
(WebCore::Font::glyphsSupportedBySmallCaps): Compute a bit vector of supported
glyphs.
(WebCore::Font::glyphsSupportedByAllSmallCaps): Ditto.
(WebCore::createDerivativeFont): Moving common code into its own helper function.
(WebCore::Font::createFontWithoutSmallCaps):
(WebCore::Font::platformCreateScaledFont): Use the common code.
* platform/graphics/mac/ComplexTextController.cpp:
(WebCore::capitalized): What is the capitalized form of a character?
(WebCore::ComplexTextController::collectComplexTextRuns): Implement the core
logic of this patch. This includes the retry when we encounter a character which
is not supported by the font feature.
* platform/spi/cocoa/CoreTextSPI.h:

LayoutTests:

Adding two new font which include lowercase characters which respond to 'smcp' and 'c2sc'.

The character mappings are:

OpenType:
'smcp': f
'c2sc': g

TrueType:
kLowerCaseType / kLowerCaseSmallCapsSelector: r
kUpperCaseType / kUpperCaseSmallCapsSelector: u

* css3/font-variant-all-expected.html:
* css3/font-variant-all.html:
* css3/font-variant-small-caps-synthesis-expected.html: Added.
* css3/font-variant-small-caps-synthesis.html: Added.
* css3/font-variant-petite-caps-synthesis-expected.html: Added.
* css3/font-variant-petite-caps-synthesis.html: Added.
* css3/resources/FontWithFeaturesLowercaseSmallCaps.otf: Added.
* css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf: Added.
* platform/mac/TestExpectations:
* platform/mac/fast/writing-mode/broken-ideograph-small-caps-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@193894 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/font-variant-all-expected.html
LayoutTests/css3/font-variant-all.html
LayoutTests/css3/font-variant-petite-caps-synthesis-coverage-expected.html [new file with mode: 0644]
LayoutTests/css3/font-variant-petite-caps-synthesis-coverage.html [new file with mode: 0644]
LayoutTests/css3/font-variant-petite-caps-synthesis-expected.html [new file with mode: 0644]
LayoutTests/css3/font-variant-petite-caps-synthesis.html [new file with mode: 0644]
LayoutTests/css3/font-variant-small-caps-synthesis-coverage-expected.html [new file with mode: 0644]
LayoutTests/css3/font-variant-small-caps-synthesis-coverage.html [new file with mode: 0644]
LayoutTests/css3/font-variant-small-caps-synthesis-expected.html [new file with mode: 0644]
LayoutTests/css3/font-variant-small-caps-synthesis.html [new file with mode: 0644]
LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.otf [new file with mode: 0644]
LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf [new file with mode: 0644]
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/mac/fast/writing-mode/broken-ideograph-small-caps-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/Font.cpp
Source/WebCore/platform/graphics/Font.h
Source/WebCore/platform/graphics/FontCascade.h
Source/WebCore/platform/graphics/FontCascadeFonts.cpp
Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm
Source/WebCore/platform/graphics/cocoa/FontCocoa.mm
Source/WebCore/platform/graphics/mac/ComplexTextController.cpp
Source/WebCore/platform/spi/cocoa/CoreTextSPI.h

index 370c852..07332c5 100644 (file)
@@ -1,3 +1,33 @@
+2015-12-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        font-variant-caps does not work if the font does not support font features
+        https://bugs.webkit.org/show_bug.cgi?id=149774
+
+        Reviewed by Antti Koivisto.
+
+        Adding two new font which include lowercase characters which respond to 'smcp' and 'c2sc'. 
+
+        The character mappings are:
+
+        OpenType:
+        'smcp': f
+        'c2sc': g
+
+        TrueType:
+        kLowerCaseType / kLowerCaseSmallCapsSelector: r
+        kUpperCaseType / kUpperCaseSmallCapsSelector: u
+
+        * css3/font-variant-all-expected.html:
+        * css3/font-variant-all.html:
+        * css3/font-variant-small-caps-synthesis-expected.html: Added.
+        * css3/font-variant-small-caps-synthesis.html: Added.
+        * css3/font-variant-petite-caps-synthesis-expected.html: Added.
+        * css3/font-variant-petite-caps-synthesis.html: Added.
+        * css3/resources/FontWithFeaturesLowercaseSmallCaps.otf: Added.
+        * css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf: Added.
+        * platform/mac/TestExpectations:
+        * platform/mac/fast/writing-mode/broken-ideograph-small-caps-expected.txt:
+
 2015-12-09  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: storage/indexeddb/objectstore-basics.html fails.
index e320e62..dbbe16d 100644 (file)
@@ -26,7 +26,7 @@
 <li>-webkit-font-variant-east-asian</li>
 </ul>
 The test passes if there is a particular sequence of checks and x characters below. Note that some x characters are expected.
-<div id="insertionpoint"><span style="font-family: FontFeaturesTestOTF;">A</span>
+<div id="insertionpoint" style="font-size: 20px;"><span style="font-family: FontFeaturesTestOTF;">A</span>
 <span style="font-family: FontFeaturesTestOTF;">A</span>
 <span style="font-family: FontFeaturesTestOTF;">B</span>
 <span style="font-family: FontFeaturesTestOTF;">B</span>
@@ -57,14 +57,8 @@ The test passes if there is a particular sequence of checks and x characters bel
 <span style="font-family: FontFeaturesTestOTF;">A</span>
 <span style="font-family: FontFeaturesTestOTF;">A</span>
 <span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
-<span style="font-family: FontFeaturesTestOTF;">A</span>
 </div>
-<div id="insertionPoint2"><span style="font-family: FontFeaturesTestTTF;">A</span>
+<div id="insertionPoint2" style="font-size: 20px;"><span style="font-family: FontFeaturesTestTTF;">A</span>
 <span style="font-family: FontFeaturesTestTTF;">A</span>
 <span style="font-family: FontFeaturesTestTTF;">B</span>
 <span style="font-family: FontFeaturesTestTTF;">B</span>
@@ -96,14 +90,8 @@ The test passes if there is a particular sequence of checks and x characters bel
 <span style="font-family: FontFeaturesTestTTF;">A</span>
 <span style="font-family: FontFeaturesTestTTF;">A</span>
 <span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
-<span style="font-family: FontFeaturesTestTTF;">A</span>
 </div>
-<div id="insertionpoint3"><span style="font-family: FontWithFeaturesOTF;">A</span>
+<div id="insertionpoint3" style="font-size: 20px;"><span style="font-family: FontWithFeaturesOTF;">A</span>
 <span style="font-family: FontWithFeaturesOTF;">A</span>
 <span style="font-family: FontWithFeaturesOTF;">B</span>
 <span style="font-family: FontWithFeaturesOTF;">B</span>
@@ -134,14 +122,8 @@ The test passes if there is a particular sequence of checks and x characters bel
 <span style="font-family: FontWithFeaturesOTF;">A</span>
 <span style="font-family: FontWithFeaturesOTF;">A</span>
 <span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
-<span style="font-family: FontWithFeaturesOTF;">A</span>
 </div>
-<div id="insertionPoint4"><span style="font-family: FontWithFeaturesTTF;">A</span>
+<div id="insertionPoint4" style="font-size: 20px;"><span style="font-family: FontWithFeaturesTTF;">A</span>
 <span style="font-family: FontWithFeaturesTTF;">A</span>
 <span style="font-family: FontWithFeaturesTTF;">B</span>
 <span style="font-family: FontWithFeaturesTTF;">B</span>
@@ -173,12 +155,6 @@ The test passes if there is a particular sequence of checks and x characters bel
 <span style="font-family: FontWithFeaturesTTF;">A</span>
 <span style="font-family: FontWithFeaturesTTF;">A</span>
 <span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
-<span style="font-family: FontWithFeaturesTTF;">A</span>
 </div>
 </body>
 </html>
index ce13f92..8c945c4 100644 (file)
 <li>-webkit-font-variant-east-asian</li>
 </ul>
 The test passes if there is a particular sequence of checks and x characters below. Note that some x characters are expected.
-<div id="insertionPoint"></div>
-<div id="insertionPoint2"></div>
-<div id="insertionPoint3"></div>
-<div id="insertionPoint4"></div>
+<div id="insertionPoint" style="font-size: 20px;"></div>
+<div id="insertionPoint2" style="font-size: 20px;"></div>
+<div id="insertionPoint3" style="font-size: 20px;"></div>
+<div id="insertionPoint4" style="font-size: 20px;"></div>
 <script>
 var insertionPoint = document.getElementById("insertionPoint");
 var insertionPoint2 = document.getElementById("insertionPoint2");
@@ -52,12 +52,6 @@ addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-ligatures", "con
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-ligatures", "no-contextual", "G");
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-position", "sub", "H");
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-position", "super", "I");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "small-caps", "J");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "all-small-caps", "J");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "all-small-caps", "K");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "petite-caps", "L");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "all-petite-caps", "L");
-addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "all-petite-caps", "M");
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "unicase", "N");
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-caps", "titling-caps", "O");
 addElement(insertionPoint, "FontFeaturesTestOTF", "font-variant-numeric", "lining-nums", "P");
@@ -91,12 +85,6 @@ addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-ligatures", "co
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-ligatures", "no-contextual", "L");
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-position", "sub", "O");
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-position", "super", "P");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "small-caps", "S");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "all-small-caps", "S");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "all-small-caps", "V");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "petite-caps", "T");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "all-petite-caps", "T");
-addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "all-petite-caps", "W");
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "unicase", "Y");
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-caps", "titling-caps", "a");
 addElement(insertionPoint2, "FontFeaturesTestTTF", "font-variant-numeric", "lining-nums", "c");
@@ -130,12 +118,6 @@ addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-ligatures", "co
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-ligatures", "no-contextual", "G");
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-position", "sub", "H");
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-position", "super", "I");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "small-caps", "J");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "all-small-caps", "J");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "all-small-caps", "K");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "petite-caps", "L");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "all-petite-caps", "L");
-addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "all-petite-caps", "M");
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "unicase", "N");
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-caps", "titling-caps", "O");
 addElement(insertionPoint3, "FontWithFeaturesOTF", "font-variant-numeric", "lining-nums", "P");
@@ -169,12 +151,6 @@ addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-ligatures", "co
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-ligatures", "no-contextual", "L");
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-position", "sub", "O");
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-position", "super", "P");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "small-caps", "S");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "all-small-caps", "S");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "all-small-caps", "V");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "petite-caps", "T");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "all-petite-caps", "T");
-addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "all-petite-caps", "W");
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "unicase", "Y");
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-caps", "titling-caps", "a");
 addElement(insertionPoint4, "FontWithFeaturesTTF", "font-variant-numeric", "lining-nums", "c");
diff --git a/LayoutTests/css3/font-variant-petite-caps-synthesis-coverage-expected.html b/LayoutTests/css3/font-variant-petite-caps-synthesis-coverage-expected.html
new file mode 100644 (file)
index 0000000..8803e56
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: "testfont";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test">ASDF</div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">AS</span>DF</div>
+<div class="test">AS<span style="font-size: 14px;">DF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+</div>
+<div style="font-family: testfont; border: solid black 1px; margin: 5px;">
+<div class="test">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span>B</div>
+<div class="test">B<span style="font-size: 14px;">A</span></div>
+<div class="test">AA</div>
+<div class="test">AA</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span>K</div>
+<div class="test">K<span style="font-size: 14px;">A</span></div>
+<div class="test">AB</div>
+<div class="test">BA</div>
+</div>
+<div style="font-family: testfont; border: solid blue 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+</div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/css3/font-variant-petite-caps-synthesis-coverage.html b/LayoutTests/css3/font-variant-petite-caps-synthesis-coverage.html
new file mode 100644 (file)
index 0000000..69b25cc
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    /* Opentype. "L" responds to pcap; "M" responds to c2pc */
+    font-family: "testfontotf";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test" style="font-variant-caps: petite-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: petite-caps;">asdf</div>
+<div class="test" style="font-variant-caps: petite-caps;">asDF</div>
+<div class="test" style="font-variant-caps: petite-caps;">ASdf</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">asdf</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">asDF</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">ASdf</div>
+</div>
+<div style="font-family: testfontotf; border: solid black 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">L</div>
+<div class="test">M</div>
+<div class="test" style="font-variant-caps: petite-caps;">a</div>
+<div class="test" style="font-variant-caps: petite-caps;">L</div>
+<div class="test" style="font-variant-caps: petite-caps;">aL</div>
+<div class="test" style="font-variant-caps: petite-caps;">La</div>
+<div class="test" style="font-variant-caps: petite-caps;">AL</div>
+<div class="test" style="font-variant-caps: petite-caps;">LA</div>
+<div class="test" style="font-variant-caps: petite-caps;">M</div>
+<div class="test" style="font-variant-caps: petite-caps;">aM</div>
+<div class="test" style="font-variant-caps: petite-caps;">Ma</div>
+<div class="test" style="font-variant-caps: petite-caps;">AM</div>
+<div class="test" style="font-variant-caps: petite-caps;">MA</div>
+</div>
+<div style="font-family: testfontotf; border: solid blue 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-petite-caps;">a</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">L</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">aL</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">La</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">AL</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">LA</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">M</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">aM</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">Ma</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">AM</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">MA</div>
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/font-variant-petite-caps-synthesis-expected.html b/LayoutTests/css3/font-variant-petite-caps-synthesis-expected.html
new file mode 100644 (file)
index 0000000..77ec1d9
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: "testfont";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test">ASDF</div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">AS</span>DF</div>
+<div class="test">AS<span style="font-size: 14px;">DF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+</div>
+<div style="font-family: testfont; border: solid black 1px; margin: 5px;">
+<div class="test">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span>B</div>
+<div class="test">B<span style="font-size: 14px;">A</span></div>
+<div class="test">AA</div>
+<div class="test">AA</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span>K</div>
+<div class="test">K<span style="font-size: 14px;">A</span></div>
+<div class="test">AB</div>
+<div class="test">BA</div>
+</div>
+<div style="font-family: testfont; border: solid blue 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/font-variant-petite-caps-synthesis.html b/LayoutTests/css3/font-variant-petite-caps-synthesis.html
new file mode 100644 (file)
index 0000000..69b25cc
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    /* Opentype. "L" responds to pcap; "M" responds to c2pc */
+    font-family: "testfontotf";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test" style="font-variant-caps: petite-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: petite-caps;">asdf</div>
+<div class="test" style="font-variant-caps: petite-caps;">asDF</div>
+<div class="test" style="font-variant-caps: petite-caps;">ASdf</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">asdf</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">asDF</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">ASdf</div>
+</div>
+<div style="font-family: testfontotf; border: solid black 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">L</div>
+<div class="test">M</div>
+<div class="test" style="font-variant-caps: petite-caps;">a</div>
+<div class="test" style="font-variant-caps: petite-caps;">L</div>
+<div class="test" style="font-variant-caps: petite-caps;">aL</div>
+<div class="test" style="font-variant-caps: petite-caps;">La</div>
+<div class="test" style="font-variant-caps: petite-caps;">AL</div>
+<div class="test" style="font-variant-caps: petite-caps;">LA</div>
+<div class="test" style="font-variant-caps: petite-caps;">M</div>
+<div class="test" style="font-variant-caps: petite-caps;">aM</div>
+<div class="test" style="font-variant-caps: petite-caps;">Ma</div>
+<div class="test" style="font-variant-caps: petite-caps;">AM</div>
+<div class="test" style="font-variant-caps: petite-caps;">MA</div>
+</div>
+<div style="font-family: testfontotf; border: solid blue 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-petite-caps;">a</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">L</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">aL</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">La</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">AL</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">LA</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">M</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">aM</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">Ma</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">AM</div>
+<div class="test" style="font-variant-caps: all-petite-caps;">MA</div>
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/font-variant-small-caps-synthesis-coverage-expected.html b/LayoutTests/css3/font-variant-small-caps-synthesis-coverage-expected.html
new file mode 100644 (file)
index 0000000..d9b6a58
--- /dev/null
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: "testfont";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test">ASDF</div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">AS</span>DF</div>
+<div class="test">AS<span style="font-size: 14px;">DF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+</div>
+<div style="font-family: testfont; border: solid black 1px; margin: 5px;">
+<div class="test">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span>B</div>
+<div class="test">B<span style="font-size: 14px;">A</span></div>
+<div class="test">AA</div>
+<div class="test">AA</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span>K</div>
+<div class="test">K<span style="font-size: 14px;">A</span></div>
+<div class="test">AB</div>
+<div class="test">BA</div>
+</div>
+<div style="font-family: testfont; border: solid red 1px; margin: 5px;">
+<div class="test"style="">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test">AA</div>
+<div class="test">AA</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A<span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span>A</div>
+</div>
+<div style="font-family: testfont; border: solid blue 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+</div>
+<div style="font-family: testfont; border: solid green 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+</div>
+</div>
+</body>
+</html>
diff --git a/LayoutTests/css3/font-variant-small-caps-synthesis-coverage.html b/LayoutTests/css3/font-variant-small-caps-synthesis-coverage.html
new file mode 100644 (file)
index 0000000..85d5c92
--- /dev/null
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+/* Only test OpenType fonts for now, because our TrueType font is busted when disabling features. */
+@font-face {
+    /* Opentype. "J" responds to smcp; "K" responds to c2sc */
+    font-family: "testfontotf";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+@font-face {
+    /* Opentype. "f" responds to smcp; "g" responds to c2sc */
+    font-family: "testfontlowercasesmallcapsotf";
+    src: url("resources/FontWithFeaturesLowercaseSmallCaps.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test" style="font-variant-caps: small-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: small-caps;">asdf</div>
+<div class="test" style="font-variant-caps: small-caps;">asDF</div>
+<div class="test" style="font-variant-caps: small-caps;">ASdf</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: all-small-caps;">asdf</div>
+<div class="test" style="font-variant-caps: all-small-caps;">asDF</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ASdf</div>
+</div>
+<div style="font-family: testfontotf; border: solid black 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">J</div>
+<div class="test">K</div>
+<div class="test" style="font-variant-caps: small-caps;">a</div>
+<div class="test" style="font-variant-caps: small-caps;">J</div>
+<div class="test" style="font-variant-caps: small-caps;">aJ</div>
+<div class="test" style="font-variant-caps: small-caps;">Ja</div>
+<div class="test" style="font-variant-caps: small-caps;">AJ</div>
+<div class="test" style="font-variant-caps: small-caps;">JA</div>
+<div class="test" style="font-variant-caps: small-caps;">K</div>
+<div class="test" style="font-variant-caps: small-caps;">aK</div>
+<div class="test" style="font-variant-caps: small-caps;">Ka</div>
+<div class="test" style="font-variant-caps: small-caps;">AK</div>
+<div class="test" style="font-variant-caps: small-caps;">KA</div>
+</div>
+<div style="font-family: testfontlowercasesmallcapsotf; border: solid red 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">f</div>
+<div class="test">g</div>
+<div class="test">F</div>
+<div class="test">G</div>
+<div class="test" style="font-variant-caps: small-caps;">a</div>
+<div class="test" style="font-variant-caps: small-caps;">f</div>
+<div class="test" style="font-variant-caps: small-caps;">af</div>
+<div class="test" style="font-variant-caps: small-caps;">fa</div>
+<div class="test" style="font-variant-caps: small-caps;">Af</div>
+<div class="test" style="font-variant-caps: small-caps;">fA</div>
+<div class="test" style="font-variant-caps: small-caps;">g</div>
+<div class="test" style="font-variant-caps: small-caps;">ag</div>
+<div class="test" style="font-variant-caps: small-caps;">ga</div>
+<div class="test" style="font-variant-caps: small-caps;">Ag</div>
+<div class="test" style="font-variant-caps: small-caps;">gA</div>
+</div>
+<div style="font-family: testfontotf; border: solid blue 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-small-caps;">a</div>
+<div class="test" style="font-variant-caps: all-small-caps;">J</div>
+<div class="test" style="font-variant-caps: all-small-caps;">aJ</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ja</div>
+<div class="test" style="font-variant-caps: all-small-caps;">AJ</div>
+<div class="test" style="font-variant-caps: all-small-caps;">JA</div>
+<div class="test" style="font-variant-caps: all-small-caps;">K</div>
+<div class="test" style="font-variant-caps: all-small-caps;">aK</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ka</div>
+<div class="test" style="font-variant-caps: all-small-caps;">AK</div>
+<div class="test" style="font-variant-caps: all-small-caps;">KA</div>
+</div>
+<div style="font-family: testfontlowercasesmallcapsotf; border: solid green 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-small-caps;">a</div>
+<div class="test" style="font-variant-caps: all-small-caps;">f</div>
+<div class="test" style="font-variant-caps: all-small-caps;">af</div>
+<div class="test" style="font-variant-caps: all-small-caps;">fa</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Af</div>
+<div class="test" style="font-variant-caps: all-small-caps;">fA</div>
+<div class="test" style="font-variant-caps: all-small-caps;">g</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ag</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ga</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ag</div>
+<div class="test" style="font-variant-caps: all-small-caps;">gA</div
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/font-variant-small-caps-synthesis-expected.html b/LayoutTests/css3/font-variant-small-caps-synthesis-expected.html
new file mode 100644 (file)
index 0000000..f04d33b
--- /dev/null
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face {
+    font-family: "testfont";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test">ASDF</div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">AS</span>DF</div>
+<div class="test">AS<span style="font-size: 14px;">DF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+<div class="test"><span style="font-size: 14px;">ASDF</span></div>
+</div>
+<div style="font-family: testfont; border: solid black 1px; margin: 5px;">
+<div class="test">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span>B</div>
+<div class="test">B<span style="font-size: 14px;">A</span></div>
+<div class="test">AA</div>
+<div class="test">AA</div>
+<div class="test">B</div>
+<div class="test"><span style="font-size: 14px;">A</span>K</div>
+<div class="test">K<span style="font-size: 14px;">A</span></div>
+<div class="test">AB</div>
+<div class="test">BA</div>
+</div>
+<div style="font-family: testfont; border: solid red 1px; margin: 5px;">
+<div class="test"style="">AB</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">B</div>
+<div class="test">A</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A<span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span>A</div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test">A<span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span>A</div>
+</div>
+<div style="font-family: testfont; border: solid blue 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+</div>
+<div style="font-family: testfont; border: solid green 1px; margin: 5px;">
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">B</span></div>
+<div class="test"><span style="font-size: 14px;">B</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+<div class="test"><span style="font-size: 14px;">A</span><span style="font-size: 14px;">A</span></div>
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/font-variant-small-caps-synthesis.html b/LayoutTests/css3/font-variant-small-caps-synthesis.html
new file mode 100644 (file)
index 0000000..85d5c92
--- /dev/null
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+/* Only test OpenType fonts for now, because our TrueType font is busted when disabling features. */
+@font-face {
+    /* Opentype. "J" responds to smcp; "K" responds to c2sc */
+    font-family: "testfontotf";
+    src: url("resources/FontWithFeatures.otf") format("opentype");
+}
+@font-face {
+    /* Opentype. "f" responds to smcp; "g" responds to c2sc */
+    font-family: "testfontlowercasesmallcapsotf";
+    src: url("resources/FontWithFeaturesLowercaseSmallCaps.otf") format("opentype");
+}
+.test {
+    font-size: 20px;
+    display: inline-block;
+}
+</style>
+</head>
+<body>
+This test makes sure that font-variant synthesis is correctly applied. The test passes when a particular pattern of Xs and checks appear below. Also note that the correct size of the character is required.
+<div style="border: solid black 1px;">
+<div class="test" style="font-variant-caps: small-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: small-caps;">asdf</div>
+<div class="test" style="font-variant-caps: small-caps;">asDF</div>
+<div class="test" style="font-variant-caps: small-caps;">ASdf</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ASDF</div>
+<div class="test" style="font-variant-caps: all-small-caps;">asdf</div>
+<div class="test" style="font-variant-caps: all-small-caps;">asDF</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ASdf</div>
+</div>
+<div style="font-family: testfontotf; border: solid black 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">J</div>
+<div class="test">K</div>
+<div class="test" style="font-variant-caps: small-caps;">a</div>
+<div class="test" style="font-variant-caps: small-caps;">J</div>
+<div class="test" style="font-variant-caps: small-caps;">aJ</div>
+<div class="test" style="font-variant-caps: small-caps;">Ja</div>
+<div class="test" style="font-variant-caps: small-caps;">AJ</div>
+<div class="test" style="font-variant-caps: small-caps;">JA</div>
+<div class="test" style="font-variant-caps: small-caps;">K</div>
+<div class="test" style="font-variant-caps: small-caps;">aK</div>
+<div class="test" style="font-variant-caps: small-caps;">Ka</div>
+<div class="test" style="font-variant-caps: small-caps;">AK</div>
+<div class="test" style="font-variant-caps: small-caps;">KA</div>
+</div>
+<div style="font-family: testfontlowercasesmallcapsotf; border: solid red 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test">AB</div>
+<div class="test">a</div>
+<div class="test">f</div>
+<div class="test">g</div>
+<div class="test">F</div>
+<div class="test">G</div>
+<div class="test" style="font-variant-caps: small-caps;">a</div>
+<div class="test" style="font-variant-caps: small-caps;">f</div>
+<div class="test" style="font-variant-caps: small-caps;">af</div>
+<div class="test" style="font-variant-caps: small-caps;">fa</div>
+<div class="test" style="font-variant-caps: small-caps;">Af</div>
+<div class="test" style="font-variant-caps: small-caps;">fA</div>
+<div class="test" style="font-variant-caps: small-caps;">g</div>
+<div class="test" style="font-variant-caps: small-caps;">ag</div>
+<div class="test" style="font-variant-caps: small-caps;">ga</div>
+<div class="test" style="font-variant-caps: small-caps;">Ag</div>
+<div class="test" style="font-variant-caps: small-caps;">gA</div>
+</div>
+<div style="font-family: testfontotf; border: solid blue 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-small-caps;">a</div>
+<div class="test" style="font-variant-caps: all-small-caps;">J</div>
+<div class="test" style="font-variant-caps: all-small-caps;">aJ</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ja</div>
+<div class="test" style="font-variant-caps: all-small-caps;">AJ</div>
+<div class="test" style="font-variant-caps: all-small-caps;">JA</div>
+<div class="test" style="font-variant-caps: all-small-caps;">K</div>
+<div class="test" style="font-variant-caps: all-small-caps;">aK</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ka</div>
+<div class="test" style="font-variant-caps: all-small-caps;">AK</div>
+<div class="test" style="font-variant-caps: all-small-caps;">KA</div>
+</div>
+<div style="font-family: testfontlowercasesmallcapsotf; border: solid green 1px; margin: 5px; font-feature-settings: 'ntrl';">
+<div class="test" style="font-variant-caps: all-small-caps;">a</div>
+<div class="test" style="font-variant-caps: all-small-caps;">f</div>
+<div class="test" style="font-variant-caps: all-small-caps;">af</div>
+<div class="test" style="font-variant-caps: all-small-caps;">fa</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Af</div>
+<div class="test" style="font-variant-caps: all-small-caps;">fA</div>
+<div class="test" style="font-variant-caps: all-small-caps;">g</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ag</div>
+<div class="test" style="font-variant-caps: all-small-caps;">ga</div>
+<div class="test" style="font-variant-caps: all-small-caps;">Ag</div>
+<div class="test" style="font-variant-caps: all-small-caps;">gA</div
+</div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.otf b/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.otf
new file mode 100644 (file)
index 0000000..1bb40ef
Binary files /dev/null and b/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.otf differ
diff --git a/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf b/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf
new file mode 100644 (file)
index 0000000..492f133
Binary files /dev/null and b/LayoutTests/css3/resources/FontWithFeaturesLowercaseSmallCaps.ttf differ
index 5c88fdd..d1520d9 100644 (file)
@@ -1287,11 +1287,8 @@ webkit.org/b/140217 http/tests/navigation/forward-and-cancel.html [ Pass Failure
 
 webkit.org/b/151469 imported/w3c/html-templates/parsing-html-templates/creating-an-element-for-the-token/template-owner-document.html [ Pass Crash ]
 
-# Temporarily disable font-features tests unil synthesis for font-variant-caps is implemented.
-webkit.org/b/149774 css3/font-feature-settings-font-face-rendering.html [ Pass Failure ImageOnlyFailure ]
-webkit.org/b/149774 css3/font-feature-settings-rendering.html [ Pass Failure ImageOnlyFailure ]
-webkit.org/b/149774 css3/font-variant-all.html [ Pass Failure ImageOnlyFailure ]
-webkit.org/b/149774 css3/font-variant-font-face-all.html [ Pass Failure ImageOnlyFailure ]
-webkit.org/b/149774 css3/font-variant-font-face-override.html [ Pass Failure ImageOnlyFailure ]
-
 webkit.org/b/152009 [ Debug ElCapitan ] fast/canvas/canvas-too-large-to-draw.html [ Skip ]
+
+# Yosemite and El Capitan do not support font feature coverage queries.
+[ Yosemite ElCapitan ] css3/font-variant-small-caps-synthesis-coverage.html [ ImageOnlyFailure ]
+[ Yosemite ElCapitan ] css3/font-variant-petite-caps-synthesis-coverage.html [ ImageOnlyFailure ]
index 78f3ddd..1864d8c 100644 (file)
@@ -13,8 +13,8 @@ layer at (0,0) size 800x377
               text run at (21,1) width 176: "\x{7B2C}\x{4E8C}\x{6BB5}\x{843D} Paragraph 2"
         RenderBlock {DIV} at (278,1) size 277x275 [bgcolor=#FFFFEE]
           RenderBlock {P} at (14,28) size 63x219 [bgcolor=#FFAAAA] [border: none (20px solid #FF8888) none (20px solid #FF8888)]
-            RenderText {#text} at (21,1) size 20x175
-              text run at (21,1) width 175: "\x{7B2C}\x{4E00}\x{6BB5}\x{843D} Paragraph 1"
+            RenderText {#text} at (21,1) size 20x156
+              text run at (21,1) width 156: "\x{7B2C}\x{4E00}\x{6BB5}\x{843D} Paragraph 1"
           RenderBlock {P} at (90,28) size 63x219 [bgcolor=#FFAAAA] [border: none (20px solid #FF8888) none (20px solid #FF8888)]
-            RenderText {#text} at (21,1) size 20x181
-              text run at (21,1) width 181: "\x{7B2C}\x{4E8C}\x{6BB5}\x{843D} Paragraph 2"
+            RenderText {#text} at (21,1) size 20x162
+              text run at (21,1) width 162: "\x{7B2C}\x{4E8C}\x{6BB5}\x{843D} Paragraph 2"
index 6694a80..9f7afc5 100644 (file)
@@ -1,3 +1,60 @@
+2015-12-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        font-variant-caps does not work if the font does not support font features
+        https://bugs.webkit.org/show_bug.cgi?id=149774
+
+        Reviewed by Antti Koivisto.
+
+        This test implements synthesis for small-caps and all-small-caps. It does so by
+        moving font variant selection into a higher level (ComplexTextController).
+        In general, the approach is to use the pure font feature until we encounter
+        a character which needs to be uppercased, and which the font feature does not
+        support uppercasing. In this situation, we try again with synthesis. In this
+        case, synthesis means artificially uppercasing letters and rendering them with
+        a smaller font.
+
+        We require system support to know which glyphs a particular font feature supports.
+        Therefore, on operating systems which do not include this support, we will simply
+        say that the font feature does not support any glyphs.
+
+        Test: css3/font-variant-small-caps-synthesis.html
+              css3/font-variant-petite-caps-synthesis.html
+
+        * platform/graphics/Font.cpp:
+        (WebCore::Font::noSmallCapsFont): Return the same font, but without smcp or c2sc.
+        This function utilizes a cache.
+        * platform/graphics/Font.h:
+        (WebCore::Font::variantFont): Small caps should never go through this function
+        anymore.
+        * platform/graphics/FontCascade.h: Because we're moving variant selection into
+        a higher level, we remove the FontVariant argument from the lower-level call.
+        * platform/graphics/FontCascadeFonts.cpp:
+        (WebCore::FontCascadeFonts::glyphDataForVariant): Use early-return style.
+        (WebCore::FontCascadeFonts::glyphDataForNormalVariant): Ditto.
+        * platform/graphics/cocoa/FontCascadeCocoa.mm:
+        (WebCore::FontCascade::fontForCombiningCharacterSequence): Because we're moving
+        variant selection into a higher level, we remove the FontVariant argument from
+        the lower-level call.
+        * platform/graphics/cocoa/FontCocoa.mm:
+        (WebCore::Font::smallCapsSupportsCharacter):
+        (WebCore::Font::allSmallCapsSupportsCharacter):
+        (WebCore::smallCapsOpenTypeDictionary): Helper function for
+        smallCapsSupportsCharacter().
+        (WebCore::smallCapsTrueTypeDictionary): Ditto.
+        (WebCore::unionBitVectors):
+        (WebCore::Font::glyphsSupportedBySmallCaps): Compute a bit vector of supported
+        glyphs.
+        (WebCore::Font::glyphsSupportedByAllSmallCaps): Ditto.
+        (WebCore::createDerivativeFont): Moving common code into its own helper function.
+        (WebCore::Font::createFontWithoutSmallCaps):
+        (WebCore::Font::platformCreateScaledFont): Use the common code.
+        * platform/graphics/mac/ComplexTextController.cpp:
+        (WebCore::capitalized): What is the capitalized form of a character?
+        (WebCore::ComplexTextController::collectComplexTextRuns): Implement the core
+        logic of this patch. This includes the retry when we encounter a character which
+        is not supported by the font feature.
+        * platform/spi/cocoa/CoreTextSPI.h:
+
 2015-12-10  Zan Dobersek  <zdobersek@igalia.com>
 
         [TexMap] Remove the TEXMAP_OPENGL_ES_2 define
index 10a30e0..e857242 100644 (file)
@@ -295,6 +295,18 @@ const Font* Font::smallCapsFont(const FontDescription& fontDescription) const
     return m_derivedFontData->smallCaps.get();
 }
 
+#if PLATFORM(COCOA)
+const Font& Font::noSynthesizableFeaturesFont() const
+{
+    if (!m_derivedFontData)
+        m_derivedFontData = std::make_unique<DerivedFontData>(isCustomFont());
+    if (!m_derivedFontData->noSynthesizableFeatures)
+        m_derivedFontData->noSynthesizableFeatures = createFontWithoutSynthesizableFeatures();
+    ASSERT(m_derivedFontData->noSynthesizableFeatures != this);
+    return *m_derivedFontData->noSynthesizableFeatures;
+}
+#endif
+
 const Font* Font::emphasisMarkFont(const FontDescription& fontDescription) const
 {
     if (!m_derivedFontData)
index c81668f..92ef542 100644 (file)
@@ -35,6 +35,8 @@
 #if ENABLE(OPENTYPE_VERTICAL)
 #include "OpenTypeVerticalData.h"
 #endif
+#include <wtf/BitVector.h>
+#include <wtf/Optional.h>
 #include <wtf/TypeCasts.h>
 #include <wtf/text/StringHash.h>
 
@@ -101,12 +103,16 @@ public:
 #endif
 
     const Font* smallCapsFont(const FontDescription&) const;
+    const Font& noSynthesizableFeaturesFont() const;
     const Font* emphasisMarkFont(const FontDescription&) const;
     const Font& brokenIdeographFont() const;
     const Font& nonSyntheticItalicFont() const;
 
     const Font* variantFont(const FontDescription& description, FontVariant variant) const
     {
+#if PLATFORM(COCOA)
+        ASSERT(variant != SmallCapsVariant);
+#endif
         switch (variant) {
         case SmallCapsVariant:
             return smallCapsFont(description);
@@ -122,6 +128,8 @@ public:
         return const_cast<Font*>(this);
     }
 
+    bool variantCapsSupportsCharacterForSynthesis(FontVariantCaps, UChar32) const;
+
     const Font& verticalRightOrientationFont() const;
     const Font& uprightOrientationFont() const;
 
@@ -193,6 +201,10 @@ public:
 #endif
 #if PLATFORM(COCOA)
     CFDictionaryRef getCFStringAttributes(bool enableKerning, FontOrientation) const;
+    const BitVector& glyphsSupportedBySmallCaps() const;
+    const BitVector& glyphsSupportedByAllSmallCaps() const;
+    const BitVector& glyphsSupportedByPetiteCaps() const;
+    const BitVector& glyphsSupportedByAllPetiteCaps() const;
 #endif
 
 #if PLATFORM(COCOA) || USE(HARFBUZZ)
@@ -226,6 +238,7 @@ private:
 
     void initCharWidths();
 
+    RefPtr<Font> createFontWithoutSynthesizableFeatures() const;
     RefPtr<Font> createScaledFont(const FontDescription&, float scaleFactor) const;
     RefPtr<Font> platformCreateScaledFont(const FontDescription&, float scaleFactor) const;
 
@@ -275,6 +288,7 @@ private:
 
         bool forCustomFont;
         RefPtr<Font> smallCaps;
+        RefPtr<Font> noSynthesizableFeatures;
         RefPtr<Font> emphasisMark;
         RefPtr<Font> brokenIdeograph;
         RefPtr<Font> verticalRightOrientation;
@@ -290,6 +304,10 @@ private:
 
 #if PLATFORM(COCOA)
     mutable HashMap<unsigned, RetainPtr<CFDictionaryRef>> m_CFStringAttributes;
+    mutable Optional<BitVector> m_glyphsSupportedBySmallCaps;
+    mutable Optional<BitVector> m_glyphsSupportedByAllSmallCaps;
+    mutable Optional<BitVector> m_glyphsSupportedByPetiteCaps;
+    mutable Optional<BitVector> m_glyphsSupportedByAllPetiteCaps;
 #endif
 
 #if PLATFORM(COCOA) || USE(HARFBUZZ)
index 6430c66..f737566 100644 (file)
@@ -187,7 +187,7 @@ public:
     GlyphData glyphDataForCharacter(UChar32, bool mirror, FontVariant = AutoVariant) const;
     
 #if PLATFORM(COCOA)
-    const Font* fontForCombiningCharacterSequence(const UChar*, size_t length, FontVariant) const;
+    const Font* fontForCombiningCharacterSequence(const UChar*, size_t length) const;
 #endif
 
     static bool isCJKIdeograph(UChar32);
index 61ea0ae..2df4347 100644 (file)
@@ -360,13 +360,13 @@ GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 c, const FontCascadeDesc
         if (fontRanges.isNull())
             break;
         GlyphData data = fontRanges.glyphDataForCharacter(c);
-        if (data.font) {
-            // The variantFont function should not normally return 0.
-            // But if it does, we will just render the capital letter big.
-            if (const Font* variantFont = data.font->variantFont(description, variant))
-                return variantFont->glyphDataForCharacter(c);
-            return data;
-        }
+        if (!data.font)
+            continue;
+        // The variantFont function should not normally return 0.
+        // But if it does, we will just render the capital letter big.
+        if (const Font* variantFont = data.font->variantFont(description, variant))
+            return variantFont->glyphDataForCharacter(c);
+        return data;
     }
 
     return glyphDataForSystemFallback(c, description, variant);
@@ -379,23 +379,23 @@ GlyphData FontCascadeFonts::glyphDataForNormalVariant(UChar32 c, const FontCasca
         if (fontRanges.isNull())
             break;
         GlyphData data = fontRanges.glyphDataForCharacter(c);
-        if (data.font) {
-            if (data.font->platformData().orientation() == Vertical && !data.font->isTextOrientationFallback()) {
-                if (!FontCascade::isCJKIdeographOrSymbol(c))
-                    return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data);
-
-                if (!data.font->hasVerticalGlyphs()) {
-                    // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
-                    // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
-                    return glyphDataForVariant(c, description, BrokenIdeographVariant, fallbackIndex);
-                }
+        if (!data.font)
+            continue;
+        if (data.font->platformData().orientation() == Vertical && !data.font->isTextOrientationFallback()) {
+            if (!FontCascade::isCJKIdeographOrSymbol(c))
+                return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data);
+
+            if (!data.font->hasVerticalGlyphs()) {
+                // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
+                // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
+                return glyphDataForVariant(c, description, BrokenIdeographVariant, fallbackIndex);
+            }
 #if PLATFORM(COCOA) || USE(CAIRO)
-                if (data.font->platformData().syntheticOblique())
-                    return glyphDataForCJKCharacterWithoutSyntheticItalic(c, data);
+            if (data.font->platformData().syntheticOblique())
+                return glyphDataForCJKCharacterWithoutSyntheticItalic(c, data);
 #endif
-            }
-            return data;
         }
+        return data;
     }
 
     return glyphDataForSystemFallback(c, description, NormalVariant);
index d1dcbd6..da03f5a 100644 (file)
@@ -689,13 +689,13 @@ int FontCascade::offsetForPositionForComplexText(const TextRun& run, float x, bo
     return controller.offsetForPosition(x, includePartialGlyphs);
 }
 
-const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length, FontVariant variant) const
+const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characters, size_t length) const
 {
     UChar32 baseCharacter;
     size_t baseCharacterLength = 0;
     U16_NEXT(characters, baseCharacterLength, length, baseCharacter);
 
-    GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, variant);
+    GlyphData baseCharacterGlyphData = glyphDataForCharacter(baseCharacter, false, NormalVariant);
 
     if (!baseCharacterGlyphData.glyph)
         return nullptr;
@@ -713,26 +713,20 @@ const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* characte
         if (baseCharacter >= 0x0600 && baseCharacter <= 0x06ff && font->shouldNotBeUsedForArabic())
             continue;
 #endif
-        if (variant == NormalVariant) {
-            if (font->platformData().orientation() == Vertical) {
-                if (isCJKIdeographOrSymbol(baseCharacter) && !font->hasVerticalGlyphs()) {
-                    variant = BrokenIdeographVariant;
-                    font = &font->brokenIdeographFont();
-                } else if (m_fontDescription.nonCJKGlyphOrientation() == NonCJKGlyphOrientation::Mixed) {
-                    const Font& verticalRightFont = font->verticalRightOrientationFont();
-                    Glyph verticalRightGlyph = verticalRightFont.glyphForCharacter(baseCharacter);
-                    if (verticalRightGlyph == baseCharacterGlyphData.glyph)
-                        font = &verticalRightFont;
-                } else {
-                    const Font& uprightFont = font->uprightOrientationFont();
-                    Glyph uprightGlyph = uprightFont.glyphForCharacter(baseCharacter);
-                    if (uprightGlyph != baseCharacterGlyphData.glyph)
-                        font = &uprightFont;
-                }
+        if (font->platformData().orientation() == Vertical) {
+            if (isCJKIdeographOrSymbol(baseCharacter) && !font->hasVerticalGlyphs())
+                font = &font->brokenIdeographFont();
+            else if (m_fontDescription.nonCJKGlyphOrientation() == NonCJKGlyphOrientation::Mixed) {
+                const Font& verticalRightFont = font->verticalRightOrientationFont();
+                Glyph verticalRightGlyph = verticalRightFont.glyphForCharacter(baseCharacter);
+                if (verticalRightGlyph == baseCharacterGlyphData.glyph)
+                    font = &verticalRightFont;
+            } else {
+                const Font& uprightFont = font->uprightOrientationFont();
+                Glyph uprightGlyph = uprightFont.glyphForCharacter(baseCharacter);
+                if (uprightGlyph != baseCharacterGlyphData.glyph)
+                    font = &uprightFont;
             }
-        } else {
-            if (const Font* variantFont = font->variantFont(m_fontDescription, variant))
-                font = variantFont;
         }
 
         if (font == baseCharacterGlyphData.font)
index 9b0d6b3..d5ebaf6 100644 (file)
@@ -40,6 +40,7 @@
 #import <float.h>
 #import <unicode/uchar.h>
 #import <wtf/Assertions.h>
+#import <wtf/NeverDestroyed.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/StdLibExtras.h>
 
@@ -281,54 +282,269 @@ void Font::platformDestroy()
 {
 }
 
-RefPtr<Font> Font::platformCreateScaledFont(const FontDescription&, float scaleFactor) const
+bool Font::variantCapsSupportsCharacterForSynthesis(FontVariantCaps fontVariantCaps, UChar32 character) const
 {
-    float size = m_platformData.size() * scaleFactor;
-
-#if USE(APPKIT)
-    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+#if (PLATFORM(IOS) && TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90300) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101104)
+    Glyph glyph = glyphForCharacter(character);
+    if (!glyph)
+        return false;
 
-    FontPlatformData scaledFontData(toCTFont([[NSFontManager sharedFontManager] convertFont:m_platformData.nsFont() toSize:size]), size, false, false, m_platformData.orientation());
+    switch (fontVariantCaps) {
+    case FontVariantCaps::Small: {
+        auto& supported = glyphsSupportedBySmallCaps();
+        return supported.size() > glyph && supported.get(glyph);
+    }
+    case FontVariantCaps::Petite: {
+        auto& supported = glyphsSupportedByPetiteCaps();
+        return supported.size() > glyph && supported.get(glyph);
+    }
+    case FontVariantCaps::AllSmall: {
+        auto& supported = glyphsSupportedByAllSmallCaps();
+        return supported.size() > glyph && supported.get(glyph);
+    }
+    case FontVariantCaps::AllPetite: {
+        auto& supported = glyphsSupportedByAllPetiteCaps();
+        return supported.size() > glyph && supported.get(glyph);
+    }
+    default:
+        // Synthesis only supports the variant-caps values listed above.
+        return true;
+    }
+#else
+    UNUSED_PARAM(character);
 
-    if (scaledFontData.font()) {
-        NSFontManager *fontManager = [NSFontManager sharedFontManager];
-        NSFontTraitMask fontTraits = [fontManager traitsOfFont:m_platformData.nsFont()];
+    switch (fontVariantCaps) {
+    case FontVariantCaps::Small:
+    case FontVariantCaps::Petite:
+    case FontVariantCaps::AllSmall:
+    case FontVariantCaps::AllPetite:
+        return false;
+    default:
+        // Synthesis only supports the variant-caps values listed above.
+        return true;
+    }
+#endif
+}
 
-        if (m_platformData.m_syntheticBold)
-            fontTraits |= NSBoldFontMask;
-        if (m_platformData.m_syntheticOblique)
-            fontTraits |= NSItalicFontMask;
+#if (PLATFORM(IOS) && TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90300) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101104)
+static RetainPtr<CFDictionaryRef> smallCapsOpenTypeDictionary(CFStringRef key, int rawValue)
+{
+    RetainPtr<CFNumberRef> value = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawValue));
+    CFTypeRef keys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
+    CFTypeRef values[] = { key, value.get() };
+    return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+}
 
-        NSFontTraitMask scaledFontTraits = [fontManager traitsOfFont:scaledFontData.nsFont()];
-        scaledFontData.m_syntheticBold = (fontTraits & NSBoldFontMask) && !(scaledFontTraits & NSBoldFontMask);
-        scaledFontData.m_syntheticOblique = (fontTraits & NSItalicFontMask) && !(scaledFontTraits & NSItalicFontMask);
+static RetainPtr<CFDictionaryRef> smallCapsTrueTypeDictionary(int rawKey, int rawValue)
+{
+    CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawKey);
+    CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawValue);
+    CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
+    CFTypeRef values[] = { key, value };
+    return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+}
 
-        return Font::create(scaledFontData);
+static void unionBitVectors(BitVector& result, CFBitVectorRef source)
+{
+    CFIndex length = CFBitVectorGetCount(source);
+    result.ensureSize(length);
+    CFIndex min = 0;
+    while (min < length) {
+        CFIndex nextIndex = CFBitVectorGetFirstIndexOfBit(source, CFRangeMake(min, length - min), 1);
+        if (nextIndex == kCFNotFound)
+            break;
+        result.set(nextIndex, true);
+        min = nextIndex + 1;
     }
-    END_BLOCK_OBJC_EXCEPTIONS;
+}
 
-    return nullptr;
-#else
-    CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
-    RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(m_platformData.font()));
-    RetainPtr<CTFontRef> scaledFont = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr));
-    FontPlatformData scaledFontData(scaledFont.get(), size, false, false, m_platformData.orientation());
+static void injectOpenTypeCoverage(CFStringRef feature, CTFontRef font, BitVector& result)
+{
+    RetainPtr<CFBitVectorRef> source = adoptCF(CTFontCopyGlyphCoverageForFeature(font, smallCapsOpenTypeDictionary(feature, 1).get()));
+    unionBitVectors(result, source.get());
+}
 
-    if (scaledFontData.font()) {
-        if (m_platformData.m_syntheticBold)
-            fontTraits |= kCTFontBoldTrait;
-        if (m_platformData.m_syntheticOblique)
-            fontTraits |= kCTFontItalicTrait;
+static void injectTrueTypeCoverage(int type, int selector, CTFontRef font, BitVector& result)
+{
+    RetainPtr<CFBitVectorRef> source = adoptCF(CTFontCopyGlyphCoverageForFeature(font, smallCapsTrueTypeDictionary(type, selector).get()));
+    unionBitVectors(result, source.get());
+}
 
-        CTFontSymbolicTraits scaledFontTraits = CTFontGetSymbolicTraits(scaledFontData.font());
-        scaledFontData.m_syntheticBold = (fontTraits & kCTFontBoldTrait) && !(scaledFontTraits & kCTFontTraitBold);
-        scaledFontData.m_syntheticOblique = (fontTraits & kCTFontItalicTrait) && !(scaledFontTraits & kCTFontTraitItalic);
+const BitVector& Font::glyphsSupportedBySmallCaps() const
+{
+    if (!m_glyphsSupportedBySmallCaps) {
+        m_glyphsSupportedBySmallCaps = BitVector();
+        injectOpenTypeCoverage(CFSTR("smcp"), platformData().font(), m_glyphsSupportedBySmallCaps.value());
+        injectTrueTypeCoverage(kLowerCaseType, kLowerCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedBySmallCaps.value());
+    }
+    return m_glyphsSupportedBySmallCaps.value();
+}
+
+const BitVector& Font::glyphsSupportedByAllSmallCaps() const
+{
+    if (!m_glyphsSupportedByAllSmallCaps) {
+        m_glyphsSupportedByAllSmallCaps = BitVector();
+        injectOpenTypeCoverage(CFSTR("smcp"), platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
+        injectOpenTypeCoverage(CFSTR("c2sc"), platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
+        injectTrueTypeCoverage(kLowerCaseType, kLowerCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
+        injectTrueTypeCoverage(kUpperCaseType, kUpperCaseSmallCapsSelector, platformData().font(), m_glyphsSupportedByAllSmallCaps.value());
+    }
+    return m_glyphsSupportedByAllSmallCaps.value();
+}
 
-        return Font::create(scaledFontData);
+const BitVector& Font::glyphsSupportedByPetiteCaps() const
+{
+    if (!m_glyphsSupportedByPetiteCaps) {
+        m_glyphsSupportedByPetiteCaps = BitVector();
+        injectOpenTypeCoverage(CFSTR("pcap"), platformData().font(), m_glyphsSupportedByPetiteCaps.value());
+        injectTrueTypeCoverage(kLowerCaseType, kLowerCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByPetiteCaps.value());
     }
+    return m_glyphsSupportedByPetiteCaps.value();
+}
 
-    return nullptr;
+const BitVector& Font::glyphsSupportedByAllPetiteCaps() const
+{
+    if (!m_glyphsSupportedByAllPetiteCaps) {
+        m_glyphsSupportedByAllPetiteCaps = BitVector();
+        injectOpenTypeCoverage(CFSTR("pcap"), platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
+        injectOpenTypeCoverage(CFSTR("c2pc"), platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
+        injectTrueTypeCoverage(kLowerCaseType, kLowerCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
+        injectTrueTypeCoverage(kUpperCaseType, kUpperCasePetiteCapsSelector, platformData().font(), m_glyphsSupportedByAllPetiteCaps.value());
+    }
+    return m_glyphsSupportedByAllPetiteCaps.value();
+}
 #endif
+
+static RefPtr<Font> createDerivativeFont(CTFontRef font, float size, FontOrientation orientation, CTFontSymbolicTraits fontTraits, bool syntheticBold, bool syntheticItalic)
+{
+    if (!font)
+        return nullptr;
+
+    FontPlatformData scaledFontData(font, size, false, false, orientation);
+
+    if (syntheticBold)
+        fontTraits |= kCTFontBoldTrait;
+    if (syntheticItalic)
+        fontTraits |= kCTFontItalicTrait;
+
+    CTFontSymbolicTraits scaledFontTraits = CTFontGetSymbolicTraits(scaledFontData.font());
+    scaledFontData.m_syntheticBold = (fontTraits & kCTFontBoldTrait) && !(scaledFontTraits & kCTFontTraitBold);
+    scaledFontData.m_syntheticOblique = (fontTraits & kCTFontItalicTrait) && !(scaledFontTraits & kCTFontTraitItalic);
+
+    return Font::create(scaledFontData);
+}
+
+static inline bool isOpenTypeFeature(CFDictionaryRef feature)
+{
+    return CFDictionaryContainsKey(feature, kCTFontOpenTypeFeatureTag) && CFDictionaryContainsKey(feature, kCTFontOpenTypeFeatureValue);
+}
+
+static inline bool isTrueTypeFeature(CFDictionaryRef feature)
+{
+    return CFDictionaryContainsKey(feature, kCTFontFeatureTypeIdentifierKey) && CFDictionaryContainsKey(feature, kCTFontFeatureSelectorIdentifierKey);
+}
+
+static inline Optional<CFStringRef> openTypeFeature(CFDictionaryRef feature)
+{
+    ASSERT(isOpenTypeFeature(feature));
+    CFStringRef tag = static_cast<CFStringRef>(CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureTag));
+    int rawValue;
+    CFNumberRef value = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureValue));
+    auto success = CFNumberGetValue(value, kCFNumberIntType, &rawValue);
+    ASSERT_UNUSED(success, success);
+    return rawValue ? Optional<CFStringRef>(tag) : Nullopt;
+}
+
+static inline std::pair<int, int> trueTypeFeature(CFDictionaryRef feature)
+{
+    ASSERT(isTrueTypeFeature(feature));
+    int rawType;
+    CFNumberRef type = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
+    auto success = CFNumberGetValue(type, kCFNumberIntType, &rawType);
+    ASSERT_UNUSED(success, success);
+    int rawSelector;
+    CFNumberRef selector = static_cast<CFNumberRef>(CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
+    success = CFNumberGetValue(selector, kCFNumberIntType, &rawSelector);
+    ASSERT_UNUSED(success, success);
+    return std::make_pair(rawType, rawSelector);
+}
+
+static inline RetainPtr<CFDictionaryRef> removedFeature(CFDictionaryRef feature)
+{
+    bool isOpenType = isOpenTypeFeature(feature);
+    bool isTrueType = isTrueTypeFeature(feature);
+    if (!isOpenType && !isTrueType)
+        return feature; // We don't understand this font format.
+    RetainPtr<CFMutableDictionaryRef> result = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+    if (isOpenType) {
+        auto featureTag = openTypeFeature(feature);
+        if (featureTag && (CFEqual(featureTag.value(), CFSTR("smcp"))
+            || CFEqual(featureTag.value(), CFSTR("c2sc"))
+            || CFEqual(featureTag.value(), CFSTR("pcap"))
+            || CFEqual(featureTag.value(), CFSTR("c2pc")))) {
+            int rawZero = 0;
+            RetainPtr<CFNumberRef> zero = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawZero));
+            CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureTag, featureTag.value());
+            CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureValue, zero.get());
+        } else {
+            CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureTag, CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureTag));
+            CFDictionaryAddValue(result.get(), kCTFontOpenTypeFeatureValue, CFDictionaryGetValue(feature, kCTFontOpenTypeFeatureValue));
+        }
+    }
+    if (isTrueType) {
+        auto trueTypeFeaturePair = trueTypeFeature(feature);
+        if (trueTypeFeaturePair.first == kLowerCaseType && (trueTypeFeaturePair.second == kLowerCaseSmallCapsSelector || trueTypeFeaturePair.second == kLowerCasePetiteCapsSelector)) {
+            int rawSelector = kDefaultLowerCaseSelector;
+            RetainPtr<CFNumberRef> selector = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawSelector));
+            CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
+            CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, selector.get());
+        } else if (trueTypeFeaturePair.first == kUpperCaseType && (trueTypeFeaturePair.second == kUpperCaseSmallCapsSelector || trueTypeFeaturePair.second == kUpperCasePetiteCapsSelector)) {
+            int rawSelector = kDefaultUpperCaseSelector;
+            RetainPtr<CFNumberRef> selector = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawSelector));
+            CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
+            CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, selector.get());
+        } else {
+            CFDictionaryAddValue(result.get(), kCTFontFeatureTypeIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureTypeIdentifierKey));
+            CFDictionaryAddValue(result.get(), kCTFontFeatureSelectorIdentifierKey, CFDictionaryGetValue(feature, kCTFontFeatureSelectorIdentifierKey));
+        }
+    }
+    return result;
+}
+
+static RetainPtr<CTFontRef> createCTFontWithoutSynthesizableFeatures(CTFontRef font)
+{
+    RetainPtr<CFArrayRef> features = static_cast<CFArrayRef>(CTFontCopyAttribute(font, kCTFontFeatureSettingsAttribute));
+    if (!features)
+        return font;
+    CFIndex featureCount = CFArrayGetCount(features.get());
+    RetainPtr<CFMutableArrayRef> newFeatures = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, featureCount, &kCFTypeArrayCallBacks));
+    for (CFIndex i = 0; i < featureCount; ++i) {
+        CFDictionaryRef feature = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(features.get(), i));
+        CFArrayAppendValue(newFeatures.get(), removedFeature(feature).get());
+    }
+    CFTypeRef keys[] = { kCTFontFeatureSettingsAttribute };
+    CFTypeRef values[] = { newFeatures.get() };
+    RetainPtr<CFDictionaryRef> attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+    RetainPtr<CTFontDescriptorRef> newDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
+    return adoptCF(CTFontCreateCopyWithAttributes(font, CTFontGetSize(font), nullptr, newDescriptor.get()));
+}
+
+RefPtr<Font> Font::createFontWithoutSynthesizableFeatures() const
+{
+    float size = m_platformData.size();
+    CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
+    RetainPtr<CTFontRef> ctFont = createCTFontWithoutSynthesizableFeatures(m_platformData.font());
+    return createDerivativeFont(ctFont.get(), size, m_platformData.orientation(), fontTraits, m_platformData.m_syntheticBold, m_platformData.m_syntheticOblique);
+}
+
+RefPtr<Font> Font::platformCreateScaledFont(const FontDescription&, float scaleFactor) const
+{
+    float size = m_platformData.size() * scaleFactor;
+    CTFontSymbolicTraits fontTraits = CTFontGetSymbolicTraits(m_platformData.font());
+    RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontCopyFontDescriptor(m_platformData.font()));
+    RetainPtr<CTFontRef> scaledFont = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), size, nullptr));
+
+    return createDerivativeFont(scaledFont.get(), size, m_platformData.orientation(), fontTraits, m_platformData.m_syntheticBold, m_platformData.m_syntheticOblique);
 }
 
 void Font::determinePitch()
index a362a30..299801f 100644 (file)
@@ -32,6 +32,7 @@
 #include "RenderText.h"
 #include "TextBreakIterator.h"
 #include "TextRun.h"
+#include <wtf/Optional.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/unicode/CharacterNames.h>
 
@@ -290,6 +291,19 @@ static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UC
     return true;
 }
 
+// FIXME: Capitalization is language-dependent and context-dependent and should operate on grapheme clusters instead of codepoints.
+static inline Optional<UChar32> capitalized(UChar32 baseCharacter)
+{
+    if (U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK)
+        return Nullopt;
+
+    UChar32 uppercaseCharacter = u_toupper(baseCharacter);
+    ASSERT(uppercaseCharacter == baseCharacter || uppercaseCharacter <= 0xFFFF);
+    if (uppercaseCharacter != baseCharacter)
+        return uppercaseCharacter;
+    return Nullopt;
+}
+
 void ComplexTextController::collectComplexTextRuns()
 {
     if (!m_end)
@@ -305,7 +319,11 @@ void ComplexTextController::collectComplexTextRuns()
     } else
         cp = m_run.characters16();
 
-    if (m_font.isSmallCaps())
+    auto fontVariantCaps = m_font.fontDescription().variantCaps();
+    bool engageAllSmallCapsProcessing = fontVariantCaps == FontVariantCaps::AllSmall || fontVariantCaps == FontVariantCaps::AllPetite;
+    bool engageSmallCapsProcessing = engageAllSmallCapsProcessing || fontVariantCaps == FontVariantCaps::Small || fontVariantCaps == FontVariantCaps::Petite;
+
+    if (engageAllSmallCapsProcessing || engageSmallCapsProcessing)
         m_smallCapsBuffer.resize(m_end);
 
     unsigned indexOfFontTransition = 0;
@@ -314,63 +332,100 @@ void ComplexTextController::collectComplexTextRuns()
 
     const Font* font;
     const Font* nextFont;
+    const Font* synthesizedFont = nullptr;
+    const Font* smallSynthesizedFont = nullptr;
 
     unsigned markCount;
-    const UChar* sequenceStart = curr;
     UChar32 baseCharacter;
     if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
         return;
 
-    UChar uppercaseCharacter = 0;
+    nextFont = m_font.fontForCombiningCharacterSequence(cp, curr - cp);
 
-    bool isSmallCaps;
-    bool nextIsSmallCaps = m_font.isSmallCaps() && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
-    ASSERT(uppercaseCharacter == 0 || u_toupper(baseCharacter) <= 0xFFFF);
+    bool isSmallCaps = false;
+    bool nextIsSmallCaps = false;
 
-    if (nextIsSmallCaps) {
-        m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
-        for (unsigned i = 0; i < markCount; ++i)
-            m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
+    auto capitalizedBase = capitalized(baseCharacter);
+    if (nextFont && nextFont != Font::systemFallback() && (capitalizedBase || engageAllSmallCapsProcessing)
+        && !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter)) {
+        synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+        smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+        m_smallCapsBuffer[0] = capitalizedBase ? capitalizedBase.value() : cp[0];
+        for (unsigned i = 1; cp + i < curr; ++i)
+            m_smallCapsBuffer[i] = cp[i];
+        nextIsSmallCaps = true;
     }
 
-    nextFont = m_font.fontForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
-
     while (curr < end) {
         font = nextFont;
         isSmallCaps = nextIsSmallCaps;
-        int index = curr - cp;
+        unsigned index = curr - cp;
 
         if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
             return;
 
-        if (m_font.isSmallCaps()) {
-            ASSERT(u_toupper(baseCharacter) <= 0xFFFF);
-            uppercaseCharacter = u_toupper(baseCharacter);
-            nextIsSmallCaps = uppercaseCharacter != baseCharacter;
-            if (nextIsSmallCaps) {
-                m_smallCapsBuffer[index] = uppercaseCharacter;
+        if (synthesizedFont) {
+            if (auto capitalizedBase = capitalized(baseCharacter)) {
+                m_smallCapsBuffer[index] = capitalizedBase.value();
                 for (unsigned i = 0; i < markCount; ++i)
                     m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
+                nextIsSmallCaps = true;
+            } else {
+                if (engageAllSmallCapsProcessing) {
+                    for (unsigned i = 0; i < curr - cp - index; ++i)
+                        m_smallCapsBuffer[index + i] = cp[index + i];
+                }
+                nextIsSmallCaps = engageAllSmallCapsProcessing;
             }
         }
 
         if (baseCharacter == zeroWidthJoiner)
             nextFont = font;
         else
-            nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
+            nextFont = m_font.fontForCombiningCharacterSequence(cp + index, curr - cp - index);
+
+        capitalizedBase = capitalized(baseCharacter);
+        if (!synthesizedFont && nextFont && nextFont != Font::systemFallback() && (capitalizedBase || engageAllSmallCapsProcessing)
+            && !nextFont->variantCapsSupportsCharacterForSynthesis(fontVariantCaps, baseCharacter)) {
+            // Rather than synthesize each character individually, we should synthesize the entire "run" if any character requires synthesis.
+            synthesizedFont = &nextFont->noSynthesizableFeaturesFont();
+            smallSynthesizedFont = synthesizedFont->smallCapsFont(m_font.fontDescription());
+            nextIsSmallCaps = true;
+            curr = cp + indexOfFontTransition;
+            continue;
+        }
 
-        if (nextFont != font) {
-            int itemStart = static_cast<int>(indexOfFontTransition);
-            int itemLength = index - indexOfFontTransition;
-            collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, font);
+        if (nextFont != font || nextIsSmallCaps != isSmallCaps) {
+            unsigned itemLength = index - indexOfFontTransition;
+            if (itemLength) {
+                unsigned itemStart = indexOfFontTransition;
+                if (synthesizedFont) {
+                    if (isSmallCaps)
+                        collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+                    else
+                        collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+                } else
+                    collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, font);
+                if (nextFont != font) {
+                    synthesizedFont = nullptr;
+                    smallSynthesizedFont = nullptr;
+                    nextIsSmallCaps = false;
+                }
+            }
             indexOfFontTransition = index;
         }
     }
 
-    int itemLength = m_end - indexOfFontTransition;
+    unsigned itemLength = m_end - indexOfFontTransition;
     if (itemLength) {
-        int itemStart = indexOfFontTransition;
-        collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, nextFont);
+        unsigned itemStart = indexOfFontTransition;
+        if (synthesizedFont) {
+            if (nextIsSmallCaps)
+                collectComplexTextRunsForCharacters(m_smallCapsBuffer.data() + itemStart, itemLength, itemStart, smallSynthesizedFont);
+            else
+                collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, synthesizedFont);
+        } else
+            collectComplexTextRunsForCharacters(cp + itemStart, itemLength, itemStart, nextFont);
     }
 
     if (!m_run.ltr())
index c85e3d5..d6c9bbe 100644 (file)
@@ -74,6 +74,7 @@ bool CTFontSetRenderingStyle(CTFontRef, CGContextRef, CGFontRenderingStyle* orig
 CTFontDescriptorRef CTFontDescriptorCreateForUIType(CTFontUIFontType, CGFloat size, CFStringRef language);
 CTFontDescriptorRef CTFontDescriptorCreateWithTextStyle(CFStringRef style, CFStringRef size, CFStringRef language);
 CTFontDescriptorRef CTFontDescriptorCreateCopyWithSymbolicTraits(CTFontDescriptorRef original, CTFontSymbolicTraits symTraitValue, CTFontSymbolicTraits symTraitMask);
+CFBitVectorRef CTFontCopyGlyphCoverageForFeature(CTFontRef, CFDictionaryRef feature);
 
 #if PLATFORM(COCOA)
 #if !USE(APPLE_INTERNAL_SDK)