[Cocoa] Migrate from CTFontTransformGlyphsWithLanguage() to CTFontShapeGlyphs()
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Aug 2020 04:23:51 +0000 (04:23 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Aug 2020 04:23:51 +0000 (04:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=215059

Reviewed by Darin Adler.

Source/WebCore:

This is in preparation for https://bugs.webkit.org/show_bug.cgi?id=214769
and https://bugs.webkit.org/show_bug.cgi?id=206208.

The solution for https://bugs.webkit.org/show_bug.cgi?id=214769 requires applying
letter-spacing after text shaping. Today, we apply letter-spacing before text shaping
which is wrong. However, if we want to apply letter-spacing after text shaping, we need
to use CTFontShapeGlyphs(), which returns the glyph -> string mapping, which allows us
to determine which glyphs to add letter-spacing to.

Updates existing tests.

Tests: fast/text/international/kana-voiced-sound-marks-1.html
       fast/text/international/kana-voiced-sound-marks-2.html

* platform/graphics/Font.cpp:
(WebCore::Font::applyTransforms const):
* platform/graphics/Font.h:
* platform/graphics/FontCascade.cpp:
(WebCore::FontCascade::widthForSimpleText const):
(WebCore::FontCascade::characterRangeCodePath):
(WebCore::FontCascade::layoutSimpleText const):
* platform/graphics/FontCascade.h:
* platform/graphics/SurrogatePairAwareTextIterator.cpp:
(WebCore::SurrogatePairAwareTextIterator::consumeSlowCase): Now that we're using
CTFontShapeGlyphs(), the shaping routine can and does look at the underlying character
string to perform character composition. This means that the glyph buffer needs to
match exactly what is in the string. We can't do any shenanigans where we pretend the
string has characters that aren't actually there.
* platform/graphics/SurrogatePairAwareTextIterator.h:
(WebCore::SurrogatePairAwareTextIterator::consume):
* platform/graphics/WidthIterator.cpp:
(WebCore::WidthIterator::shouldApplyFontTransforms const):
(WebCore::WidthIterator::applyFontTransforms): Reversing the glyph buffer for rtl
content needs to be done inside platform-specific code, because its behavior depends on
which platform shaping routine is being used.
(WebCore::WidthIterator::commitCurrentFontRange):
(WebCore::WidthIterator::advanceInternal):
* platform/graphics/WidthIterator.h:
* platform/graphics/cocoa/FontCocoa.mm:
(WebCore::Font::applyTransforms const):
* platform/graphics/mac/SimpleFontDataCoreText.cpp:
(WebCore::Font::getCFStringAttributes const):

Source/WebCore/PAL:

* pal/spi/cocoa/CoreTextSPI.h:

Source/WTF:

* wtf/PlatformUse.h: Rename CTFONTTRANSFORMGLYPHSWITHLANGUAGE to CTFONTSHAPEGLYPHS,
because that's the new function.

LayoutTests:

* fast/encoding/denormalised-voiced-japanese-chars-expected.html: Copied from LayoutTests/fast/encoding/denormalised-voiced-japanese-chars.html.
* fast/encoding/denormalised-voiced-japanese-chars.html: Update to be a reftest.
* fast/text/international/kana-voiced-sound-marks-1-expected.html: Copied from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
Make the test more robust.
* fast/text/international/kana-voiced-sound-marks-1.html: Renamed from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks.html.
Make the test more robust.
* fast/text/international/kana-voiced-sound-marks-2-expected.html: Copied from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
Make the test more robust.
* fast/text/international/kana-voiced-sound-marks-2.html: Renamed from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
Make the test more robust.
* fast/text/soft-hyphen-min-preferred-width-expected.html:
* fast/text/soft-hyphen-min-preferred-width.html: Update to be more robust.
* platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png: Removed.
* platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
* platform/ios/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
* platform/mac-bigsur/svg/W3C-I18N/tspan-direction-rtl-expected.txt: Added Big Sur -expected result.
* platform/mac-bigsur/svg/text/bidi-tspans-expected.txt: Ditto.
* platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png: Removed.
* platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
* platform/win/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
* platform/wincairo/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
* platform/wpe/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.

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

34 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/encoding/denormalised-voiced-japanese-chars-expected.html [new file with mode: 0644]
LayoutTests/fast/encoding/denormalised-voiced-japanese-chars.html
LayoutTests/fast/text/international/kana-voiced-sound-marks-1-expected.html [moved from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html with 72% similarity]
LayoutTests/fast/text/international/kana-voiced-sound-marks-1.html [moved from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks.html with 60% similarity]
LayoutTests/fast/text/international/kana-voiced-sound-marks-2-expected.html [new file with mode: 0644]
LayoutTests/fast/text/international/kana-voiced-sound-marks-2.html [new file with mode: 0644]
LayoutTests/fast/text/soft-hyphen-min-preferred-width-expected.html
LayoutTests/fast/text/soft-hyphen-min-preferred-width.html
LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png [deleted file]
LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
LayoutTests/platform/ios/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
LayoutTests/platform/mac-bigsur/svg/W3C-I18N/tspan-direction-rtl-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac-bigsur/svg/text/bidi-tspans-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png [deleted file]
LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
LayoutTests/platform/win/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
LayoutTests/platform/wincairo/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
LayoutTests/platform/wpe/fast/encoding/denormalised-voiced-japanese-chars-expected.txt [deleted file]
Source/WTF/ChangeLog
Source/WTF/wtf/PlatformUse.h
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/spi/cocoa/CoreTextSPI.h
Source/WebCore/platform/graphics/Font.cpp
Source/WebCore/platform/graphics/Font.h
Source/WebCore/platform/graphics/FontCascade.cpp
Source/WebCore/platform/graphics/FontCascade.h
Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.cpp
Source/WebCore/platform/graphics/SurrogatePairAwareTextIterator.h
Source/WebCore/platform/graphics/WidthIterator.cpp
Source/WebCore/platform/graphics/WidthIterator.h
Source/WebCore/platform/graphics/cocoa/FontCocoa.mm
Source/WebCore/platform/graphics/mac/SimpleFontDataCoreText.cpp

index 1de4bb5..d01ee5c 100644 (file)
@@ -1,3 +1,33 @@
+2020-08-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [Cocoa] Migrate from CTFontTransformGlyphsWithLanguage() to CTFontShapeGlyphs()
+        https://bugs.webkit.org/show_bug.cgi?id=215059
+
+        Reviewed by Darin Adler.
+
+        * fast/encoding/denormalised-voiced-japanese-chars-expected.html: Copied from LayoutTests/fast/encoding/denormalised-voiced-japanese-chars.html.
+        * fast/encoding/denormalised-voiced-japanese-chars.html: Update to be a reftest.
+        * fast/text/international/kana-voiced-sound-marks-1-expected.html: Copied from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
+        Make the test more robust.
+        * fast/text/international/kana-voiced-sound-marks-1.html: Renamed from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks.html.
+        Make the test more robust.
+        * fast/text/international/kana-voiced-sound-marks-2-expected.html: Copied from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
+        Make the test more robust.
+        * fast/text/international/kana-voiced-sound-marks-2.html: Renamed from LayoutTests/imported/blink/fast/text/international/kana-voiced-sound-marks-expected.html.
+        Make the test more robust.
+        * fast/text/soft-hyphen-min-preferred-width-expected.html:
+        * fast/text/soft-hyphen-min-preferred-width.html: Update to be more robust.
+        * platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png: Removed.
+        * platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+        * platform/ios/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+        * platform/mac-bigsur/svg/W3C-I18N/tspan-direction-rtl-expected.txt: Added Big Sur -expected result.
+        * platform/mac-bigsur/svg/text/bidi-tspans-expected.txt: Ditto.
+        * platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png: Removed.
+        * platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+        * platform/win/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+        * platform/wincairo/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+        * platform/wpe/fast/encoding/denormalised-voiced-japanese-chars-expected.txt: Removed.
+
 2020-08-10  Lauro Moura  <lmoura@igalia.com>
 
         [GTK][WPE] Gardening failures and rebaseline some tests.
diff --git a/LayoutTests/fast/encoding/denormalised-voiced-japanese-chars-expected.html b/LayoutTests/fast/encoding/denormalised-voiced-japanese-chars-expected.html
new file mode 100644 (file)
index 0000000..dff300f
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the same as the precomposed version.
+<h2 style="font-family: 'Hiragino Mincho ProN';">バナナとパナマ</h2>
+
+</body>
+</html>
index 93791ee..f5da115 100644 (file)
@@ -4,12 +4,8 @@
 <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
 </head>
 <body>
-This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are identical.
-<h2>バナナとパナマ</h2>
-<p>The above is decomposed</p>
-
-<h2>バナナとパナマ</h2>
-<p>The above is precomposed</p>
+This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the same as the precomposed version.
+<h2 style="font-family: 'Hiragino Mincho ProN';">バナナとパナマ</h2>
 
 </body>
 </html>
 <div>
 <p>Test passes if two blocks are identical.
 <div class=test>
-<div>ばびぶべぼ</div>
-<div>ぱぴぷぺぽ</div>
+<div>ばび</div>
+<div>ぱぴ</div>
 </div>
 <div class=test>
-<div>ばびぶべぼ</div>
-<div>ぱぴぷぺぽ</div>
+<div>ばび</div>
+<div>ぱぴ</div>
 </div>
 </div>
 <div>
 <p>Test passes if two blocks are identical.
 <div class=test>
-<div>は&#x3099;ひ&#x3099;ふ&#x3099;へ&#x3099;ほ&#x3099;</div>
-<div>は&#x309A;ひ&#x309A;ふ&#x309A;へ&#x309A;ほ&#x309A;</div>
+<div>は&#x3099;ひ&#x3099;</div>
+<div>は&#x309A;ひ&#x309A;</div>
 </div>
 <div class=test>
-<div>ばびぶべぼ</div>
-<div>ぱぴぷぺぽ</div>
+<div>ばび</div>
+<div>ぱぴ</div>
 </div>
 </div>
diff --git a/LayoutTests/fast/text/international/kana-voiced-sound-marks-2-expected.html b/LayoutTests/fast/text/international/kana-voiced-sound-marks-2-expected.html
new file mode 100644 (file)
index 0000000..22fe6ea
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+.test {
+  border:solid 1px;
+  display:inline-block;
+  font-family:"Hiragino Kaku Gothic Pro","MS Gothic";
+  font-size:25px;
+}
+</style>
+<div>
+<p>Test passes if two blocks are identical.
+<div class=test>
+<div>ぶべぼ</div>
+<div>ぷぺぽ</div>
+</div>
+<div class=test>
+<div>ぶべぼ</div>
+<div>ぷぺぽ</div>
+</div>
+</div>
diff --git a/LayoutTests/fast/text/international/kana-voiced-sound-marks-2.html b/LayoutTests/fast/text/international/kana-voiced-sound-marks-2.html
new file mode 100644 (file)
index 0000000..0719bdd
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+.test {
+  border:solid 1px;
+  display:inline-block;
+  font-family:"Hiragino Kaku Gothic Pro","MS Gothic";
+  font-size:25px;
+}
+</style>
+<div>
+<p>Test passes if two blocks are identical.
+<div class=test>
+<div>ふ&#x3099;へ&#x3099;ほ&#x3099;</div>
+<div>ふ&#x309A;へ&#x309A;ほ&#x309A;</div>
+</div>
+<div class=test>
+<div>ぶべぼ</div>
+<div>ぷぺぽ</div>
+</div>
+</div>
index 9847126..9fb7eba 100644 (file)
@@ -11,7 +11,7 @@
 <table cellspacing=0 cellpadding=0>
     <tr>
         <td>
-            <div>extraordinar<span>-</span><br>ily</div>
+            <div>extraordina-<br>rily</div>
         </td>
         <td style="width: 100%"></td>
     </tr>
index de8b1ca..a650385 100644 (file)
@@ -11,7 +11,7 @@
 <table cellspacing=0 cellpadding=0>
     <tr>
         <td>
-            <div style="-webkit-hyphens: manual;">extraordinar&shy;ily</div>
+            <div style="-webkit-hyphens: manual;">extraordina&shy;rily</div>
         </td>
         <td style="width: 100%"></td>
     </tr>
diff --git a/LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png b/LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png
deleted file mode 100644 (file)
index c3521d3..0000000
Binary files a/LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.png and /dev/null differ
diff --git a/LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/gtk/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index 19e3d0f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x248
-  RenderBlock {HTML} at (0,0) size 800x248
-    RenderBody {BODY} at (8,8) size 784x224
-      RenderBlock (anonymous) at (0,0) size 784x54
-        RenderText {#text} at (0,0) size 775x53
-          text run at (0,0) width 775: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the"
-          text run at (0,18) width 724: "same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are"
-          text run at (0,36) width 57: "identical."
-      RenderBlock {H2} at (0,73) size 784x28
-        RenderText {#text} at (0,0) size 175x26
-          text run at (0,0) width 175: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,120) size 784x19
-        RenderText {#text} at (0,0) size 164x17
-          text run at (0,0) width 164: "The above is decomposed"
-      RenderBlock {H2} at (0,158) size 784x28
-        RenderText {#text} at (0,0) size 175x26
-          text run at (0,0) width 175: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,205) size 784x19
-        RenderText {#text} at (0,0) size 169x17
-          text run at (0,0) width 169: "The above is precomposed"
diff --git a/LayoutTests/platform/ios/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/ios/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index d8a35a4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x264
-  RenderBlock {HTML} at (0,0) size 800x264
-    RenderBody {BODY} at (8,8) size 784x240
-      RenderBlock (anonymous) at (0,0) size 784x60
-        RenderText {#text} at (0,0) size 766x59
-          text run at (0,0) width 766: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered"
-          text run at (0,20) width 763: "the same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are"
-          text run at (0,40) width 60: "identical."
-      RenderBlock {H2} at (0,79) size 784x31
-        RenderText {#text} at (0,1) size 168x28
-          text run at (0,1) width 168: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,129) size 784x21
-        RenderText {#text} at (0,0) size 166x19
-          text run at (0,0) width 166: "The above is decomposed"
-      RenderBlock {H2} at (0,169) size 784x31
-        RenderText {#text} at (0,1) size 168x28
-          text run at (0,1) width 168: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,219) size 784x21
-        RenderText {#text} at (0,0) size 172x19
-          text run at (0,0) width 172: "The above is precomposed"
diff --git a/LayoutTests/platform/mac-bigsur/svg/W3C-I18N/tspan-direction-rtl-expected.txt b/LayoutTests/platform/mac-bigsur/svg/W3C-I18N/tspan-direction-rtl-expected.txt
new file mode 100644 (file)
index 0000000..5342ed1
--- /dev/null
@@ -0,0 +1,28 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (0,0) size 800x600
+    RenderSVGContainer {g} at (33,222) size 651x143
+      RenderSVGText {text} at (92,133) size 276x21 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (191,0) size 85x21
+          chunk 1 (middle anchor) text run 1 at (283.65,150.00) startOffset 0 endOffset 14 width 84.15 RTL: "\x{646}\x{634}\x{627}\x{637} \x{627}\x{644}\x{62A}\x{62F}\x{648}\x{64A}\x{644} \""
+        RenderSVGTSpan {tspan} at (0,0) size 181x21
+          RenderSVGInlineText {#text} at (11,0) size 181x21
+            chunk 1 (middle anchor) text run 1 at (103.60,150.00) startOffset 0 endOffset 8 width 44.98 RTL: ", \x{627}\x{62E}\x{62A}\x{628}\x{627}\x{631}"
+            chunk 1 (middle anchor) text run 1 at (148.58,150.00) startOffset 0 endOffset 14 width 135.07: "dirRTL ubEmbed"
+        RenderSVGInlineText {#text} at (0,0) size 12x21
+          chunk 1 (middle anchor) text run 1 at (92.21,150.00) startOffset 0 endOffset 2 width 11.39 RTL: "\"!"
+      RenderSVGText {text} at (20,170) size 87x13 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (0,0) size 87x12
+          chunk 1 text run 1 at (20.00,180.00) startOffset 0 endOffset 18 width 86.18: "Reference graphic:"
+      RenderSVGImage {image} at (100,300) size 584x65
+    RenderSVGContainer {g} at (16,557) size 73x12
+      RenderSVGText {text} at (10,334) size 44x8 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (0,0) size 44x7
+          chunk 1 text run 1 at (10.00,340.00) startOffset 0 endOffset 16 width 43.03: "$Revision: 1.7 $"
+    RenderSVGRect {rect} at (0,0) size 800x600 [stroke={[type=SOLID] [color=#000000]}] [x=1.00] [y=1.00] [width=478.00] [height=358.00]
+    RenderSVGContainer {g} at (0,0) size 800x38
+      RenderSVGRect {rect} at (0,0) size 800x36 [stroke={[type=SOLID] [color=#000000]}] [fill={[type=SOLID] [color=#FF0000]}] [x=1.00] [y=1.00] [width=478.00] [height=20.00]
+      RenderSVGText {text} at (206,0) size 68x23 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (0,0) size 68x23
+          chunk 1 (middle anchor) text run 1 at (206.46,18.00) startOffset 0 endOffset 5 width 67.09: "DRAFT"
diff --git a/LayoutTests/platform/mac-bigsur/svg/text/bidi-tspans-expected.txt b/LayoutTests/platform/mac-bigsur/svg/text/bidi-tspans-expected.txt
new file mode 100644 (file)
index 0000000..a25ad2d
--- /dev/null
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (153,222) size 460x85
+    RenderSVGContainer {g} at (153,222) size 460x85
+      RenderSVGText {text} at (92,133) size 276x21 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (0,0) size 276x21
+          chunk 1 (middle anchor) text run 1 at (92.21,150.00) startOffset 0 endOffset 10 width 56.37 RTL: ", \x{627}\x{62E}\x{62A}\x{628}\x{627}\x{631}\"!"
+          chunk 1 (middle anchor) text run 1 at (148.58,150.00) startOffset 0 endOffset 14 width 135.07: "dirRTL ubEmbed"
+          chunk 1 (middle anchor) text run 1 at (283.65,150.00) startOffset 0 endOffset 14 width 84.15 RTL: "\x{646}\x{634}\x{627}\x{637} \x{627}\x{644}\x{62A}\x{62F}\x{648}\x{64A}\x{644} \""
+      RenderSVGText {text} at (92,163) size 276x21 contains 1 chunk(s)
+        RenderSVGInlineText {#text} at (191,0) size 85x21
+          chunk 1 (middle anchor) text run 1 at (283.65,180.00) startOffset 0 endOffset 14 width 84.15 RTL: "\x{646}\x{634}\x{627}\x{637} \x{627}\x{644}\x{62A}\x{62F}\x{648}\x{64A}\x{644} \""
+        RenderSVGTSpan {tspan} at (0,0) size 181x21
+          RenderSVGInlineText {#text} at (11,0) size 181x21
+            chunk 1 (middle anchor) text run 1 at (103.60,180.00) startOffset 0 endOffset 8 width 44.98 RTL: ", \x{627}\x{62E}\x{62A}\x{628}\x{627}\x{631}"
+            chunk 1 (middle anchor) text run 1 at (148.58,180.00) startOffset 0 endOffset 14 width 135.07: "dirRTL ubEmbed"
+        RenderSVGInlineText {#text} at (0,0) size 12x21
+          chunk 1 (middle anchor) text run 1 at (92.21,180.00) startOffset 0 endOffset 2 width 11.39 RTL: "\"!"
diff --git a/LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png b/LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png
deleted file mode 100644 (file)
index a386f36..0000000
Binary files a/LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.png and /dev/null differ
diff --git a/LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/mac/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index dbd670f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x260
-  RenderBlock {HTML} at (0,0) size 800x260
-    RenderBody {BODY} at (8,8) size 784x236
-      RenderBlock (anonymous) at (0,0) size 784x54
-        RenderText {#text} at (0,0) size 766x54
-          text run at (0,0) width 766: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered"
-          text run at (0,18) width 763: "the same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are"
-          text run at (0,36) width 60: "identical."
-      RenderBlock {H2} at (0,73) size 784x34
-        RenderText {#text} at (0,3) size 168x28
-          text run at (0,3) width 168: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,126) size 784x19
-        RenderText {#text} at (0,0) size 166x18
-          text run at (0,0) width 166: "The above is decomposed"
-      RenderBlock {H2} at (0,164) size 784x34
-        RenderText {#text} at (0,3) size 168x28
-          text run at (0,3) width 168: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,217) size 784x19
-        RenderText {#text} at (0,0) size 172x18
-          text run at (0,0) width 172: "The above is precomposed"
diff --git a/LayoutTests/platform/win/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/win/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index 68a5973..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x242
-      RenderBlock (anonymous) at (0,0) size 784x54
-        RenderText {#text} at (0,0) size 775x54
-          text run at (0,0) width 775: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the"
-          text run at (0,18) width 724: "same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are"
-          text run at (0,36) width 57: "identical."
-      RenderBlock {H2} at (0,73) size 784x37
-        RenderText {#text} at (0,5) size 168x28
-          text run at (0,5) width 168: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,129) size 784x19
-        RenderText {#text} at (0,0) size 164x18
-          text run at (0,0) width 164: "The above is decomposed"
-      RenderBlock {H2} at (0,167) size 784x37
-        RenderText {#text} at (0,5) size 168x28
-          text run at (0,5) width 168: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,223) size 784x19
-        RenderText {#text} at (0,0) size 169x18
-          text run at (0,0) width 169: "The above is precomposed"
diff --git a/LayoutTests/platform/wincairo/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/wincairo/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index f41dbf7..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x238
-  RenderBlock {HTML} at (0,0) size 800x238
-    RenderBody {BODY} at (8,8) size 784x214
-      RenderBlock (anonymous) at (0,0) size 784x40
-        RenderText {#text} at (0,0) size 784x39
-          text run at (0,0) width 784: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the same"
-          text run at (0,20) width 717: "as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are identical."
-      RenderBlock {H2} at (0,59) size 784x28
-        RenderText {#text} at (0,0) size 164x26
-          text run at (0,0) width 164: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,106) size 784x21
-        RenderText {#text} at (0,0) size 159x19
-          text run at (0,0) width 159: "The above is decomposed"
-      RenderBlock {H2} at (0,146) size 784x28
-        RenderText {#text} at (0,0) size 164x26
-          text run at (0,0) width 164: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,193) size 784x21
-        RenderText {#text} at (0,0) size 164x19
-          text run at (0,0) width 164: "The above is precomposed"
diff --git a/LayoutTests/platform/wpe/fast/encoding/denormalised-voiced-japanese-chars-expected.txt b/LayoutTests/platform/wpe/fast/encoding/denormalised-voiced-japanese-chars-expected.txt
deleted file mode 100644 (file)
index ad6d490..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x248
-  RenderBlock {HTML} at (0,0) size 800x248
-    RenderBody {BODY} at (8,8) size 784x224
-      RenderBlock (anonymous) at (0,0) size 784x54
-        RenderText {#text} at (0,0) size 775x53
-          text run at (0,0) width 775: "This test checks that the decomposed unicode version of voiced japanese hiragana and katakana characters are rendered the"
-          text run at (0,18) width 724: "same as the precomposed version. This test is a pixel-test, and passes when the text in the two heading elements are"
-          text run at (0,36) width 57: "identical."
-      RenderBlock {H2} at (0,73) size 784x28
-        RenderText {#text} at (0,0) size 168x26
-          text run at (0,0) width 168: "\x{30CF}\x{3099}\x{30CA}\x{30CA}\x{3068}\x{30CF}\x{309A}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,120) size 784x19
-        RenderText {#text} at (0,0) size 164x17
-          text run at (0,0) width 164: "The above is decomposed"
-      RenderBlock {H2} at (0,158) size 784x28
-        RenderText {#text} at (0,0) size 168x26
-          text run at (0,0) width 168: "\x{30D0}\x{30CA}\x{30CA}\x{3068}\x{30D1}\x{30CA}\x{30DE}"
-      RenderBlock {P} at (0,205) size 784x19
-        RenderText {#text} at (0,0) size 169x17
-          text run at (0,0) width 169: "The above is precomposed"
index 54e4ead..a3df3d4 100644 (file)
@@ -1,3 +1,13 @@
+2020-08-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [Cocoa] Migrate from CTFontTransformGlyphsWithLanguage() to CTFontShapeGlyphs()
+        https://bugs.webkit.org/show_bug.cgi?id=215059
+
+        Reviewed by Darin Adler.
+
+        * wtf/PlatformUse.h: Rename CTFONTTRANSFORMGLYPHSWITHLANGUAGE to CTFONTSHAPEGLYPHS,
+        because that's the new function.
+
 2020-08-09  Ben Nham  <nham@apple.com>
 
         Preload graphics drivers in Mac WebProcess
index eaa8285..ade42dd 100644 (file)
 #endif
 
 #if PLATFORM(COCOA) && !(PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 110000)
-#define USE_CTFONTTRANSFORMGLYPHSWITHLANGUAGE 1
+#define USE_CTFONTSHAPEGLYPHS 1
 #endif
index 8720e5c..19783a1 100644 (file)
@@ -1,3 +1,53 @@
+2020-08-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [Cocoa] Migrate from CTFontTransformGlyphsWithLanguage() to CTFontShapeGlyphs()
+        https://bugs.webkit.org/show_bug.cgi?id=215059
+
+        Reviewed by Darin Adler.
+
+        This is in preparation for https://bugs.webkit.org/show_bug.cgi?id=214769
+        and https://bugs.webkit.org/show_bug.cgi?id=206208.
+
+        The solution for https://bugs.webkit.org/show_bug.cgi?id=214769 requires applying
+        letter-spacing after text shaping. Today, we apply letter-spacing before text shaping
+        which is wrong. However, if we want to apply letter-spacing after text shaping, we need
+        to use CTFontShapeGlyphs(), which returns the glyph -> string mapping, which allows us
+        to determine which glyphs to add letter-spacing to.
+
+        Updates existing tests.
+
+        Tests: fast/text/international/kana-voiced-sound-marks-1.html
+               fast/text/international/kana-voiced-sound-marks-2.html
+
+        * platform/graphics/Font.cpp:
+        (WebCore::Font::applyTransforms const):
+        * platform/graphics/Font.h:
+        * platform/graphics/FontCascade.cpp:
+        (WebCore::FontCascade::widthForSimpleText const):
+        (WebCore::FontCascade::characterRangeCodePath):
+        (WebCore::FontCascade::layoutSimpleText const):
+        * platform/graphics/FontCascade.h:
+        * platform/graphics/SurrogatePairAwareTextIterator.cpp:
+        (WebCore::SurrogatePairAwareTextIterator::consumeSlowCase): Now that we're using
+        CTFontShapeGlyphs(), the shaping routine can and does look at the underlying character
+        string to perform character composition. This means that the glyph buffer needs to
+        match exactly what is in the string. We can't do any shenanigans where we pretend the
+        string has characters that aren't actually there.
+        * platform/graphics/SurrogatePairAwareTextIterator.h:
+        (WebCore::SurrogatePairAwareTextIterator::consume):
+        * platform/graphics/WidthIterator.cpp:
+        (WebCore::WidthIterator::shouldApplyFontTransforms const):
+        (WebCore::WidthIterator::applyFontTransforms): Reversing the glyph buffer for rtl
+        content needs to be done inside platform-specific code, because its behavior depends on
+        which platform shaping routine is being used.
+        (WebCore::WidthIterator::commitCurrentFontRange):
+        (WebCore::WidthIterator::advanceInternal):
+        * platform/graphics/WidthIterator.h:
+        * platform/graphics/cocoa/FontCocoa.mm:
+        (WebCore::Font::applyTransforms const):
+        * platform/graphics/mac/SimpleFontDataCoreText.cpp:
+        (WebCore::Font::getCFStringAttributes const):
+
 2020-08-10  Devin Rousso  <drousso@apple.com>
 
         Add quirk to force touch events on mail.yahoo.com
index 38c67b5..abb5e68 100644 (file)
@@ -1,3 +1,12 @@
+2020-08-10  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [Cocoa] Migrate from CTFontTransformGlyphsWithLanguage() to CTFontShapeGlyphs()
+        https://bugs.webkit.org/show_bug.cgi?id=215059
+
+        Reviewed by Darin Adler.
+
+        * pal/spi/cocoa/CoreTextSPI.h:
+
 2020-08-07  John Wilander  <wilander@apple.com>
 
         Experimental: Cap the expiry of persistent cookies set in 3rd-party CNAME cloaked HTTP responses
index 7d214ed..342d591 100644 (file)
@@ -46,6 +46,12 @@ typedef CF_OPTIONS(uint32_t, CTFontTransformOptions) {
     kCTFontTransformApplyPositioning = (1 << 1)
 };
 
+typedef CF_OPTIONS(CFOptionFlags, CTFontShapeOptions) {
+    kCTFontShapeWithKerning = (1 << 0),
+    kCTFontShapeWithClusterComposition = (1 << 1),
+    kCTFontShapeRightToLeft = (1 << 2),
+};
+
 typedef CF_OPTIONS(uint32_t, CTFontDescriptorOptions) {
     kCTFontDescriptorOptionSystemUIFont = 1 << 1,
     kCTFontDescriptorOptionPreferAppleSystemFont = kCTFontOptionsPreferSystemFont
@@ -92,7 +98,7 @@ extern const CFStringRef kCTFontCSSFamilyMonospace;
 extern const CFStringRef kCTFontCSSFamilySystemUI;
 
 bool CTFontTransformGlyphs(CTFontRef, CGGlyph glyphs[], CGSize advances[], CFIndex count, CTFontTransformOptions);
-CGSize CTFontTransformGlyphsWithLanguage(CTFontRef, CGGlyph[], CGSize[], CFIndex count, CTFontTransformOptions, CFStringRef language, void (^handler)(CFRange, CGGlyph**, CGSize**));
+CGSize CTFontShapeGlyphs(CTFontRef, CGGlyph glyphs[], CGSize advances[], CGPoint origins[], CFIndex indexes[], const UniChar chars[], CFIndex count, CTFontShapeOptions, CFStringRef language, void (^handler)(CFRange, CGGlyph**, CGSize**, CGPoint**, CFIndex**));
 
 CGSize CTRunGetInitialAdvance(CTRunRef);
 CTLineRef CTLineCreateWithUniCharProvider(CTUniCharProviderCallback, CTUniCharDisposeCallback, void* refCon);
index 9ae6906..6ef9789 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005, 2008, 2010, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2020 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Alexey Proskuryakov
  *
  * Redistribution and use in source and binary forms, with or without
@@ -512,7 +512,7 @@ RefPtr<Font> Font::createScaledFont(const FontDescription& fontDescription, floa
 }
 
 #if !PLATFORM(COCOA)
-void Font::applyTransforms(GlyphBuffer&, unsigned, bool, bool, const AtomString&) const
+void Font::applyTransforms(GlyphBuffer&, unsigned, unsigned, bool, bool, const AtomString&, StringView, TextDirection) const
 {
 }
 #endif
index eaf9dac..8ec3380 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the internal font implementation.
  *
- * Copyright (C) 2006, 2008, 2010, 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2020 Apple Inc. All rights reserved.
  * Copyright (C) 2007-2008 Torch Mobile, Inc.
  *
  * This library is free software; you can redistribute it and/or
@@ -207,7 +207,7 @@ public:
 #endif
 
     bool canRenderCombiningCharacterSequence(const UChar*, size_t) const;
-    void applyTransforms(GlyphBuffer&, unsigned beginningIndex, bool enableKerning, bool requiresShaping, const AtomString& locale) const;
+    void applyTransforms(GlyphBuffer&, unsigned beginningGlyphIndex, unsigned beginningStringIndex, bool enableKerning, bool requiresShaping, const AtomString& locale, StringView text, TextDirection) const;
 
 #if PLATFORM(WIN)
     SCRIPT_FONTPROPERTIES* scriptFontProperties() const;
index 84f6b80..cd6dedb 100644 (file)
@@ -426,7 +426,7 @@ float FontCascade::width(const TextRun& run, HashSet<const Font*>* fallbackFonts
     return result;
 }
 
-float FontCascade::widthForSimpleText(StringView text) const
+float FontCascade::widthForSimpleText(StringView text, TextDirection textDirection) const
 {
     if (text.isNull() || text.isEmpty())
         return 0;
@@ -445,7 +445,7 @@ float FontCascade::widthForSimpleText(StringView text) const
         glyphBuffer.add(glyph, font, glyphWidth, i);
     }
 
-    font.applyTransforms(glyphBuffer, 0, enableKerning(), requiresShaping(), fontDescription().computedLocale());
+    font.applyTransforms(glyphBuffer, 0, 0, enableKerning(), requiresShaping(), fontDescription().computedLocale(), text, textDirection);
     // This is needed only to match the result of the slow path.
     // Same glyph widths but different floating point arithmetic can produce different run width.
     float runWidthDifferenceWithTransformApplied = -runWidth;
@@ -748,6 +748,11 @@ FontCascade::CodePath FontCascade::characterRangeCodePath(const UChar* character
         if (c <= 0x302F)
             return Complex;
 
+        if (c < 0x3099)
+            continue;
+        if (c < 0x309D)
+            return Complex; // KATAKANA-HIRAGANA (SEMI-)VOICED SOUND MARKS require character composition
+
         if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic
             continue;
         if (c <= 0xA67D)
@@ -1391,6 +1396,8 @@ GlyphBuffer FontCascade::layoutSimpleText(const TextRun& run, unsigned from, uns
     // FIXME: Deal with the GlyphBuffer's current initialAdvance.
     glyphBuffer.setInitialAdvance(FloatSize(initialAdvance, 0));
 
+    // The glyph buffer is currently in logical order,
+    // but we need to return the results in visual order.
     if (run.rtl())
         glyphBuffer.reverse(0, glyphBuffer.size());
 
index 9ad3ae4..387399f 100644 (file)
@@ -116,7 +116,7 @@ public:
 
     float widthOfTextRange(const TextRun&, unsigned from, unsigned to, HashSet<const Font*>* fallbackFonts = 0, float* outWidthBeforeRange = nullptr, float* outWidthAfterRange = nullptr) const;
     WEBCORE_EXPORT float width(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
-    float widthForSimpleText(StringView text) const;
+    float widthForSimpleText(StringView text, TextDirection = TextDirection::LTR) const;
 
     std::unique_ptr<TextLayout, TextLayoutDeleter> createLayout(RenderText&, float xPos, bool collapseWhiteSpace) const;
     static float width(TextLayout&, unsigned from, unsigned len, HashSet<const Font*>* fallbackFonts = 0);
index 4cb9093..5aee4cc 100644 (file)
@@ -37,19 +37,7 @@ SurrogatePairAwareTextIterator::SurrogatePairAwareTextIterator(const UChar* char
 
 bool SurrogatePairAwareTextIterator::consumeSlowCase(UChar32& character, unsigned& clusterLength)
 {
-    if (character <= 0x30FE) {
-        // Deal with Hiragana and Katakana voiced and semi-voiced syllables.
-        // Normalize into composed form, and then look for glyph with base + combined mark.
-        // Check above for character range to minimize performance impact.
-        if (UChar32 normalized = normalizeVoicingMarks()) {
-            character = normalized;
-            clusterLength = 2;
-        }
-        return true;
-    }
-
-    if (!U16_IS_SURROGATE(character))
-        return true;
+    ASSERT(U16_IS_SURROGATE(character));
 
     // If we have a surrogate pair, make sure it starts with the high part.
     if (!U16_IS_SURROGATE_LEAD(character))
index 470d14a..354fc99 100644 (file)
@@ -40,7 +40,7 @@ public:
         character = *m_characters;
         clusterLength = 1;
 
-        if (character < HiraganaLetterSmallA)
+        if (!U16_IS_SURROGATE(character))
             return true;
 
         return consumeSlowCase(character, clusterLength);
index d76895f..e018e62 100644 (file)
@@ -78,24 +78,23 @@ static inline bool isSoftBankEmoji(UChar32 codepoint)
     return codepoint >= 0xE001 && codepoint <= 0xE537;
 }
 
-inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer& glyphBuffer, unsigned lastGlyphCount, UChar32 previousCharacter) const -> TransformsType
+inline auto WidthIterator::shouldApplyFontTransforms(const GlyphBuffer& glyphBuffer, unsigned lastGlyphCount, unsigned currentCharacterIndex) const -> TransformsType
 {
-    if (glyphBuffer.size() == (lastGlyphCount + 1) && isSoftBankEmoji(previousCharacter))
+    ASSERT(currentCharacterIndex <= m_run.length());
+    if (glyphBuffer.size() == (lastGlyphCount + 1) && currentCharacterIndex && isSoftBankEmoji(m_run.text()[currentCharacterIndex - 1]))
         return TransformsType::Forced;
     if (m_run.length() <= 1)
         return TransformsType::None;
     return TransformsType::NotForced;
 }
 
-inline float WidthIterator::applyFontTransforms(GlyphBuffer& glyphBuffer, bool ltr, unsigned& lastGlyphCount, const Font& font, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace)
+inline float WidthIterator::applyFontTransforms(GlyphBuffer& glyphBuffer, unsigned lastGlyphCount, unsigned currentCharacterIndex, const Font& font, bool force, CharactersTreatedAsSpace& charactersTreatedAsSpace)
 {
-    ASSERT_UNUSED(previousCharacter, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter) != WidthIterator::TransformsType::None);
+    ASSERT_UNUSED(currentCharacterIndex, shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, currentCharacterIndex) != WidthIterator::TransformsType::None);
 
     auto glyphBufferSize = glyphBuffer.size();
-    if (!force && glyphBufferSize <= lastGlyphCount + 1) {
-        lastGlyphCount = glyphBufferSize;
+    if (!force && glyphBufferSize <= lastGlyphCount + 1)
         return 0;
-    }
 
     GlyphBufferAdvance* advances = glyphBuffer.advances(0);
     float beforeWidth = 0;
@@ -103,17 +102,15 @@ inline float WidthIterator::applyFontTransforms(GlyphBuffer& glyphBuffer, bool l
         beforeWidth += advances[i].width();
 
     ASSERT(lastGlyphCount <= glyphBufferSize);
-    if (!ltr)
-        glyphBuffer.reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
 
-    font.applyTransforms(glyphBuffer, lastGlyphCount, m_enableKerning, m_requiresShaping, m_font.fontDescription().computedLocale());
+    font.applyTransforms(glyphBuffer, lastGlyphCount, m_currentCharacterIndex, m_enableKerning, m_requiresShaping, m_font.fontDescription().computedLocale(), m_run.text(), m_run.direction());
     glyphBufferSize = glyphBuffer.size();
 
-    for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
+    GlyphBufferOrigin* origins = glyphBuffer.origins(0);
+    for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) {
         advances[i].setHeight(-advances[i].height());
-
-    if (!ltr)
-        glyphBuffer.reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
+        origins[i].setY(-origins[i].y());
+    }
 
     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i) {
         auto characterIndex = glyphBuffer.stringOffsetAt(i);
@@ -133,7 +130,6 @@ inline float WidthIterator::applyFontTransforms(GlyphBuffer& glyphBuffer, bool l
     for (unsigned i = lastGlyphCount; i < glyphBufferSize; ++i)
         afterWidth += advances[i].width();
 
-    lastGlyphCount = glyphBufferSize;
     return afterWidth - beforeWidth;
 }
 
@@ -166,11 +162,12 @@ static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treat
     return std::make_pair(expandLeft, expandRight);
 }
 
-void WidthIterator::commitCurrentFontRange(GlyphBuffer& glyphBuffer, unsigned lastGlyphCount, const Font& font, UChar32 previousCharacter, const Font& primaryFont, UChar32 character, float widthOfCurrentFontRange, CharactersTreatedAsSpace& charactersTreatedAsSpace)
+void WidthIterator::commitCurrentFontRange(GlyphBuffer& glyphBuffer, unsigned lastGlyphCount, unsigned currentCharacterIndex, const Font& font, const Font& primaryFont, UChar32 character, float widthOfCurrentFontRange, CharactersTreatedAsSpace& charactersTreatedAsSpace)
 {
-    auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
+    auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, currentCharacterIndex);
     if (transformsType != TransformsType::None)
-        m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, font, previousCharacter, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
+        m_runWidthSoFar += applyFontTransforms(glyphBuffer, lastGlyphCount, currentCharacterIndex, font, transformsType == TransformsType::Forced, charactersTreatedAsSpace);
+    m_currentCharacterIndex = currentCharacterIndex;
 
     if (widthOfCurrentFontRange && m_fallbackFonts && &font != &primaryFont) {
         // FIXME: This does a little extra work that could be avoided if
@@ -188,7 +185,7 @@ void WidthIterator::commitCurrentFontRange(GlyphBuffer& glyphBuffer, unsigned la
 template <typename TextIterator>
 inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer& glyphBuffer)
 {
-    // The core logic here needs to match SimpleLineLayout::widthForSimpleText()
+    // The core logic here needs to match FontCascade::widthForSimpleText()
     bool rtl = m_run.rtl();
     bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
 
@@ -204,8 +201,8 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
     const Font* lastFontData = &primaryFont;
     unsigned lastGlyphCount = glyphBuffer.size();
 
+    auto currentCharacterIndex = textIterator.currentIndex();
     UChar32 character = 0;
-    UChar32 previousCharacter = 0;
     unsigned clusterLength = 0;
     CharactersTreatedAsSpace charactersTreatedAsSpace;
     float widthOfCurrentFontRange = 0;
@@ -218,14 +215,15 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
         // doesn't support the code point. We should ignore them at this point to ensure they are not displayed.
         if (!characterMustDrawSomething) {
             textIterator.advance(advanceLength);
+            currentCharacterIndex = textIterator.currentIndex();
             continue;
         }
 #endif
-        int currentCharacterIndex = textIterator.currentIndex();
         const GlyphData& glyphData = m_font.glyphDataForCharacter(character, rtl);
         Glyph glyph = glyphData.glyph;
         if (!glyph && !characterMustDrawSomething) {
             textIterator.advance(advanceLength);
+            currentCharacterIndex = textIterator.currentIndex();
             continue;
         }
         const Font* font = glyphData.font ? glyphData.font : &m_font.primaryFont();
@@ -243,8 +241,7 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
         }
 
         if (font != lastFontData) {
-            commitCurrentFontRange(glyphBuffer, lastGlyphCount, *lastFontData, previousCharacter, primaryFont, character, widthOfCurrentFontRange, charactersTreatedAsSpace);
-            m_currentCharacterIndex = currentCharacterIndex;
+            commitCurrentFontRange(glyphBuffer, lastGlyphCount, currentCharacterIndex, *lastFontData, primaryFont, character, widthOfCurrentFontRange, charactersTreatedAsSpace);
             lastGlyphCount = glyphBuffer.size();
             lastFontData = font;
             widthOfCurrentFontRange = width;
@@ -289,6 +286,8 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
                                     glyphBuffer.add(font->zeroWidthSpaceGlyph(), *font, m_expansionPerOpportunity, currentCharacterIndex);
                                 else
                                     glyphBuffer.add(font->spaceGlyph(), *font, m_expansionPerOpportunity, currentCharacterIndex);
+                                lastGlyphCount = glyphBuffer.size();
+                                m_currentCharacterIndex = currentCharacterIndex;
                             } else
                                 glyphBuffer.expandLastAdvance(m_expansionPerOpportunity);
                         } else {
@@ -314,7 +313,7 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
                 m_isAfterExpansion = false;
         }
 
-        auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, previousCharacter);
+        auto transformsType = shouldApplyFontTransforms(glyphBuffer, lastGlyphCount, currentCharacterIndex);
         if (transformsType != TransformsType::None && FontCascade::treatAsSpace(character)) {
             charactersTreatedAsSpace.constructAndAppend(
                 currentCharacterIndex,
@@ -332,23 +331,22 @@ inline void WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuff
         if (m_forTextEmphasis && !FontCascade::canReceiveTextEmphasis(character))
             glyph = 0;
 
+        glyphBuffer.add(glyph, *font, width, currentCharacterIndex);
+
         // Advance past the character we just dealt with.
         textIterator.advance(advanceLength);
+        currentCharacterIndex = textIterator.currentIndex();
 
         m_runWidthSoFar += width;
 
-        glyphBuffer.add(glyph, *font, width, currentCharacterIndex);
-
         if (m_accountForGlyphBounds) {
             m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
             m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
             m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
         }
-        previousCharacter = character;
     }
 
-    commitCurrentFontRange(glyphBuffer, lastGlyphCount, *lastFontData, previousCharacter, primaryFont, character, widthOfCurrentFontRange, charactersTreatedAsSpace);
-    m_currentCharacterIndex = textIterator.currentIndex();
+    commitCurrentFontRange(glyphBuffer, lastGlyphCount, currentCharacterIndex, *lastFontData, primaryFont, character, widthOfCurrentFontRange, charactersTreatedAsSpace);
 
     if (leftoverJustificationWidth) {
         if (m_forTextEmphasis)
index 1798b1b..f45d6d2 100644 (file)
@@ -60,9 +60,9 @@ private:
     inline void advanceInternal(TextIterator&, GlyphBuffer&);
 
     enum class TransformsType { None, Forced, NotForced };
-    TransformsType shouldApplyFontTransforms(const GlyphBuffer&, unsigned lastGlyphCount, UChar32 previousCharacter) const;
-    float applyFontTransforms(GlyphBuffer&, bool ltr, unsigned& lastGlyphCount, const Font&, UChar32 previousCharacter, bool force, CharactersTreatedAsSpace&);
-    void commitCurrentFontRange(GlyphBuffer&, unsigned lastGlyphCount, const Font&, UChar32 previousCharacter, const Font& primaryFont, UChar32 character, float widthOfCurrentFontRange, CharactersTreatedAsSpace&);
+    TransformsType shouldApplyFontTransforms(const GlyphBuffer&, unsigned lastGlyphCount, unsigned currentCharacterIndex) const;
+    float applyFontTransforms(GlyphBuffer&, unsigned lastGlyphCount, unsigned currentCharacterIndex, const Font&, bool force, CharactersTreatedAsSpace&);
+    void commitCurrentFontRange(GlyphBuffer&, unsigned lastGlyphCount, unsigned currentCharacterIndex, const Font&, const Font& primaryFont, UChar32 character, float widthOfCurrentFontRange, CharactersTreatedAsSpace&);
 
     const FontCascade& m_font;
     const TextRun& m_run;
index 65b5e16..93fb000 100644 (file)
@@ -544,28 +544,78 @@ RefPtr<Font> Font::platformCreateScaledFont(const FontDescription&, float scaleF
     return createDerivativeFont(scaledFont.get(), size, m_platformData.orientation(), fontTraits, m_platformData.syntheticBold(), m_platformData.syntheticOblique());
 }
 
-void Font::applyTransforms(GlyphBuffer& glyphBuffer, unsigned beginningIndex, bool enableKerning, bool requiresShaping, const AtomString& locale) const
+void Font::applyTransforms(GlyphBuffer& glyphBuffer, unsigned beginningGlyphIndex, unsigned beginningStringIndex, bool enableKerning, bool requiresShaping, const AtomString& locale, StringView text, TextDirection textDirection) const
 {
-    // FIXME: Implement GlyphBuffer initial advance.
     UNUSED_PARAM(requiresShaping);
-    CTFontTransformOptions options = (enableKerning ? kCTFontTransformApplyPositioning : 0) | kCTFontTransformApplyShaping;
-#if USE(CTFONTTRANSFORMGLYPHSWITHLANGUAGE)
-    auto handler = ^(CFRange range, CGGlyph** newGlyphsPointer, CGSize** newAdvancesPointer) {
+
+    // FIXME: Implement GlyphBuffer initial advance.
+#if USE(CTFONTSHAPEGLYPHS)
+    auto handler = ^(CFRange range, CGGlyph** newGlyphsPointer, CGSize** newAdvancesPointer, CGPoint** newOffsetsPointer, CFIndex** newIndicesPointer) {
         range.location = std::min(std::max(range.location, static_cast<CFIndex>(0)), static_cast<CFIndex>(glyphBuffer.size()));
         if (range.length < 0) {
             range.length = std::min(range.location, -range.length);
             range.location = range.location - range.length;
-            glyphBuffer.remove(beginningIndex + range.location, range.length);
+            glyphBuffer.remove(beginningGlyphIndex + range.location, range.length);
         } else
-            glyphBuffer.makeHole(beginningIndex + range.location, range.length, this);
-        *newGlyphsPointer = glyphBuffer.glyphs(beginningIndex);
-        *newAdvancesPointer = glyphBuffer.advances(beginningIndex);
+            glyphBuffer.makeHole(beginningGlyphIndex + range.location, range.length, this);
+
+        *newGlyphsPointer = glyphBuffer.glyphs(beginningGlyphIndex);
+        *newAdvancesPointer = glyphBuffer.advances(beginningGlyphIndex);
+        *newOffsetsPointer = glyphBuffer.origins(beginningGlyphIndex);
+        *newIndicesPointer = glyphBuffer.offsetsInString(beginningGlyphIndex);
     };
-    CTFontTransformGlyphsWithLanguage(m_platformData.ctFont(), glyphBuffer.glyphs(beginningIndex), reinterpret_cast<CGSize*>(glyphBuffer.advances(beginningIndex)), glyphBuffer.size() - beginningIndex, options, LocaleCocoa::canonicalLanguageIdentifierFromString(locale).string().createCFString().get(), handler);
+
+    auto substring = text.substring(beginningStringIndex);
+    auto upconvertedCharacters = substring.upconvertedCharacters();
+    auto localeString = LocaleCocoa::canonicalLanguageIdentifierFromString(locale).string().createCFString();
+    CTFontShapeOptions options = kCTFontShapeWithClusterComposition
+        | (enableKerning ? kCTFontShapeWithKerning : 0)
+        | (textDirection == TextDirection::RTL ? kCTFontShapeRightToLeft : 0);
+
+    for (unsigned i = 0; i < glyphBuffer.size() - beginningGlyphIndex; ++i)
+        glyphBuffer.offsetsInString(beginningGlyphIndex)[i] -= beginningStringIndex;
+
+    CTFontShapeGlyphs(
+        m_platformData.ctFont(),
+        glyphBuffer.glyphs(beginningGlyphIndex),
+        reinterpret_cast<CGSize*>(glyphBuffer.advances(beginningGlyphIndex)),
+        reinterpret_cast<CGPoint*>(glyphBuffer.origins(beginningGlyphIndex)),
+        glyphBuffer.offsetsInString(beginningGlyphIndex),
+        reinterpret_cast<const UniChar*>(upconvertedCharacters.get()),
+        glyphBuffer.size() - beginningGlyphIndex,
+        options,
+        localeString.get(),
+        handler);
+
+    for (unsigned i = 0; i < glyphBuffer.size() - beginningGlyphIndex; ++i)
+        glyphBuffer.offsetsInString(beginningGlyphIndex)[i] += beginningStringIndex;
+
 #else
+
+    UNUSED_PARAM(beginningStringIndex);
     UNUSED_PARAM(locale);
-    CTFontTransformGlyphs(m_platformData.ctFont(), glyphBuffer.glyphs(beginningIndex), reinterpret_cast<CGSize*>(glyphBuffer.advances(beginningIndex)), glyphBuffer.size() - beginningIndex, options);
+    UNUSED_PARAM(text);
+
+    // CTFontTransformGlyphs() operates in visual order, but WidthIterator iterates in logical order.
+    // Temporarily put us in visual order just for the call, then put us back into logical order when
+    // the call is done.
+    // We don't have a global view of the entire GlyphBuffer; we're just operating on a single chunk of it.
+    // WidthIterator encounters the chunks out in logical order, so we have to maintain that invariant.
+    // Eventually, FontCascade::layoutSimpleText() will reverse the whole buffer to put the entire thing
+    // in visual order, but that's okay because it has a view of the entire GlyphBuffer.
+    // On the other hand, CTFontShapeGlyphs() accepts the buffer in logical order but returns it in physical
+    // order, which means the second reverse() in this function still needs to execute when
+    // CTFontShapeGlyphs() is being used.
+    if (textDirection == TextDirection::RTL)
+        glyphBuffer.reverse(beginningGlyphIndex, glyphBuffer.size() - beginningGlyphIndex);
+
+    CTFontTransformOptions options = (enableKerning ? kCTFontTransformApplyPositioning : 0) | kCTFontTransformApplyShaping;
+    CTFontTransformGlyphs(m_platformData.ctFont(), glyphBuffer.glyphs(beginningGlyphIndex), reinterpret_cast<CGSize*>(glyphBuffer.advances(beginningGlyphIndex)), glyphBuffer.size() - beginningGlyphIndex, options);
 #endif
+
+    // See the comment above in this function where the other call to reverse() is.
+    if (textDirection == TextDirection::RTL)
+        glyphBuffer.reverse(beginningGlyphIndex, glyphBuffer.size() - beginningGlyphIndex);
 }
 
 static int extractNumber(CFNumberRef number)
index 32579da..ff6fc8d 100644 (file)
@@ -41,7 +41,7 @@ RetainPtr<CFDictionaryRef> Font::getCFStringAttributes(bool enableKerning, FontO
     values[0] = platformData().ctFont();
     size_t count = 1;
 
-#if USE(CTFONTTRANSFORMGLYPHSWITHLANGUAGE)
+#if USE(CTFONTSHAPEGLYPHS)
     RetainPtr<CFStringRef> localeString;
     if (!locale.isEmpty()) {
         localeString = locale.string().createCFString();