<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[182236] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/182236">182236</a></dd>
<dt>Author</dt> <dd>mmaxfield@apple.com</dd>
<dt>Date</dt> <dd>2015-04-01 09:58:20 -0700 (Wed, 01 Apr 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Support forcing expansion opportunities at the beginning and ending of a run
https://bugs.webkit.org/show_bug.cgi?id=143292
Reviewed by David Hyatt.
Migrate ExpansionBehavior to an enum comprised of two values: one for leading behavior
and one for trailing behavior. Each of these two values can take on a value of
"force," "forbid," and neither. All calls that interact with justification are
migrated to use this new structure.
Note that in this terminology, "leading" and "trailing" are with respect to the order
that elements get laid out in a line. Therefore, leading is always on the left, since
lines get laid out that way regardless of their bidi attributes.
This is getting ready for https://bugs.webkit.org/show_bug.cgi?id=142608
No new tests because there is no behavior change.
* platform/graphics/FontCascade.cpp:
(WebCore::FontCascade::expansionOpportunityCountInternal): Migrate to these new
values.
(WebCore::FontCascade::expansionOpportunityCount): Ditto.
(WebCore::FontCascade::leadingExpansionOpportunity): Returns whether one is present
or not.
(WebCore::FontCascade::trailingExpansionOpportunity): Ditto.
* platform/graphics/FontCascade.h:
* platform/graphics/GlyphBuffer.h: New leading expansion field.
(WebCore::GlyphBuffer::setLeadingExpansion):
(WebCore::GlyphBuffer::leadingExpansion):
* platform/graphics/TextRun.h: m_expansionBehavior needs more bits.
(WebCore::TextRun::expansionBehavior):
(WebCore::TextRun::allowsLeadingExpansion): Deleted.
(WebCore::TextRun::allowsTrailingExpansion): Deleted.
* platform/graphics/WidthIterator.cpp: Update to support new type.
(WebCore::WidthIterator::WidthIterator):
(WebCore::expansionLocation): Where should we insert expansions?
(WebCore::WidthIterator::advanceInternal): Use expansionLocation()
* platform/graphics/cocoa/FontCascadeCocoa.mm:
(WebCore::FontCascade::adjustSelectionRectForComplexText):
(WebCore::FontCascade::getGlyphsAndAdvancesForComplexText):
* platform/graphics/mac/ComplexTextController.cpp: Same as WidthIterator
(WebCore::ComplexTextController::ComplexTextController):
(WebCore::ComplexTextController::advance):
(WebCore::expansionLocation):
(WebCore::ComplexTextController::adjustGlyphsAndAdvances):
* platform/graphics/mac/ComplexTextController.h:
(WebCore::ComplexTextController::leadingExpansion):
* platform/text/TextFlags.h: Add new enum values
* rendering/InlineBox.h: Update to include new values.
(WebCore::InlineBox::InlineBoxBitfields::InlineBoxBitfields):
(WebCore::InlineBox::canHaveTrailingExpansion):
(WebCore::InlineBox::setCanHaveTrailingExpansion):
(WebCore::InlineBox::setForceTrailingExpansion):
(WebCore::InlineBox::forceTrailingExpansion):
(WebCore::InlineBox::setForceLeadingExpansion):
(WebCore::InlineBox::forceLeadingExpansion):
* rendering/InlineTextBox.h:
* rendering/RenderBlockLineLayout.cpp: Update to use new FontCascade signatures.
(WebCore::expansionBehaviorForInlineTextBox):
(WebCore::applyExpansionBehavior):
(WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontCascadecpp">trunk/Source/WebCore/platform/graphics/FontCascade.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontCascadeh">trunk/Source/WebCore/platform/graphics/FontCascade.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsGlyphBufferh">trunk/Source/WebCore/platform/graphics/GlyphBuffer.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsTextRunh">trunk/Source/WebCore/platform/graphics/TextRun.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsWidthIteratorcpp">trunk/Source/WebCore/platform/graphics/WidthIterator.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicscocoaFontCascadeCocoamm">trunk/Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsmacComplexTextControllercpp">trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsmacComplexTextControllerh">trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h</a></li>
<li><a href="#trunkSourceWebCoreplatformtextTextFlagsh">trunk/Source/WebCore/platform/text/TextFlags.h</a></li>
<li><a href="#trunkSourceWebCorerenderingInlineBoxh">trunk/Source/WebCore/rendering/InlineBox.h</a></li>
<li><a href="#trunkSourceWebCorerenderingInlineTextBoxh">trunk/Source/WebCore/rendering/InlineTextBox.h</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderBlockLineLayoutcpp">trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/ChangeLog        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -1,3 +1,67 @@
</span><ins>+2015-04-01 Myles C. Maxfield <mmaxfield@apple.com>
+
+ Support forcing expansion opportunities at the beginning and ending of a run
+ https://bugs.webkit.org/show_bug.cgi?id=143292
+
+ Reviewed by David Hyatt.
+
+ Migrate ExpansionBehavior to an enum comprised of two values: one for leading behavior
+ and one for trailing behavior. Each of these two values can take on a value of
+ "force," "forbid," and neither. All calls that interact with justification are
+ migrated to use this new structure.
+
+ Note that in this terminology, "leading" and "trailing" are with respect to the order
+ that elements get laid out in a line. Therefore, leading is always on the left, since
+ lines get laid out that way regardless of their bidi attributes.
+
+ This is getting ready for https://bugs.webkit.org/show_bug.cgi?id=142608
+
+ No new tests because there is no behavior change.
+
+ * platform/graphics/FontCascade.cpp:
+ (WebCore::FontCascade::expansionOpportunityCountInternal): Migrate to these new
+ values.
+ (WebCore::FontCascade::expansionOpportunityCount): Ditto.
+ (WebCore::FontCascade::leadingExpansionOpportunity): Returns whether one is present
+ or not.
+ (WebCore::FontCascade::trailingExpansionOpportunity): Ditto.
+ * platform/graphics/FontCascade.h:
+ * platform/graphics/GlyphBuffer.h: New leading expansion field.
+ (WebCore::GlyphBuffer::setLeadingExpansion):
+ (WebCore::GlyphBuffer::leadingExpansion):
+ * platform/graphics/TextRun.h: m_expansionBehavior needs more bits.
+ (WebCore::TextRun::expansionBehavior):
+ (WebCore::TextRun::allowsLeadingExpansion): Deleted.
+ (WebCore::TextRun::allowsTrailingExpansion): Deleted.
+ * platform/graphics/WidthIterator.cpp: Update to support new type.
+ (WebCore::WidthIterator::WidthIterator):
+ (WebCore::expansionLocation): Where should we insert expansions?
+ (WebCore::WidthIterator::advanceInternal): Use expansionLocation()
+ * platform/graphics/cocoa/FontCascadeCocoa.mm:
+ (WebCore::FontCascade::adjustSelectionRectForComplexText):
+ (WebCore::FontCascade::getGlyphsAndAdvancesForComplexText):
+ * platform/graphics/mac/ComplexTextController.cpp: Same as WidthIterator
+ (WebCore::ComplexTextController::ComplexTextController):
+ (WebCore::ComplexTextController::advance):
+ (WebCore::expansionLocation):
+ (WebCore::ComplexTextController::adjustGlyphsAndAdvances):
+ * platform/graphics/mac/ComplexTextController.h:
+ (WebCore::ComplexTextController::leadingExpansion):
+ * platform/text/TextFlags.h: Add new enum values
+ * rendering/InlineBox.h: Update to include new values.
+ (WebCore::InlineBox::InlineBoxBitfields::InlineBoxBitfields):
+ (WebCore::InlineBox::canHaveTrailingExpansion):
+ (WebCore::InlineBox::setCanHaveTrailingExpansion):
+ (WebCore::InlineBox::setForceTrailingExpansion):
+ (WebCore::InlineBox::forceTrailingExpansion):
+ (WebCore::InlineBox::setForceLeadingExpansion):
+ (WebCore::InlineBox::forceLeadingExpansion):
+ * rendering/InlineTextBox.h:
+ * rendering/RenderBlockLineLayout.cpp: Update to use new FontCascade signatures.
+ (WebCore::expansionBehaviorForInlineTextBox):
+ (WebCore::applyExpansionBehavior):
+ (WebCore::RenderBlockFlow::computeInlineDirectionPositionsForSegment):
+
</ins><span class="cx"> 2015-04-01 Zalan Bujtas <zalan@apple.com>
</span><span class="cx">
</span><span class="cx"> Lots of time spent querying table cell borders, when there are none.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsFontCascadecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/FontCascade.cpp (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/FontCascade.cpp        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/FontCascade.cpp        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -1005,9 +1005,14 @@
</span><span class="cx"> return isCJKIdeograph(c);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-unsigned FontCascade::expansionOpportunityCountInternal(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
</del><ins>+std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const LChar* characters, size_t length, TextDirection direction, ExpansionBehavior expansionBehavior)
</ins><span class="cx"> {
</span><span class="cx"> unsigned count = 0;
</span><ins>+ bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion;
+ if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ }
</ins><span class="cx"> if (direction == LTR) {
</span><span class="cx"> for (size_t i = 0; i < length; ++i) {
</span><span class="cx"> if (treatAsSpace(characters[i])) {
</span><span class="lines">@@ -1025,13 +1030,25 @@
</span><span class="cx"> isAfterExpansion = false;
</span><span class="cx"> }
</span><span class="cx"> }
</span><del>- return count;
</del><ins>+ if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) {
+ --count;
+ isAfterExpansion = false;
+ }
+ return std::make_pair(count, isAfterExpansion);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-unsigned FontCascade::expansionOpportunityCountInternal(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
</del><ins>+std::pair<unsigned, bool> FontCascade::expansionOpportunityCountInternal(const UChar* characters, size_t length, TextDirection direction, ExpansionBehavior expansionBehavior)
</ins><span class="cx"> {
</span><span class="cx"> static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText();
</span><span class="cx"> unsigned count = 0;
</span><ins>+ bool isAfterExpansion = (expansionBehavior & LeadingExpansionMask) == ForbidLeadingExpansion;
+ if ((expansionBehavior & LeadingExpansionMask) == ForceLeadingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ }
</ins><span class="cx"> if (direction == LTR) {
</span><span class="cx"> for (size_t i = 0; i < length; ++i) {
</span><span class="cx"> UChar32 character = characters[i];
</span><span class="lines">@@ -1075,16 +1092,65 @@
</span><span class="cx"> isAfterExpansion = false;
</span><span class="cx"> }
</span><span class="cx"> }
</span><del>- return count;
</del><ins>+ if (!isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForceTrailingExpansion) {
+ ++count;
+ isAfterExpansion = true;
+ } else if (isAfterExpansion && (expansionBehavior & TrailingExpansionMask) == ForbidTrailingExpansion) {
+ --count;
+ isAfterExpansion = false;
+ }
+ return std::make_pair(count, isAfterExpansion);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-unsigned FontCascade::expansionOpportunityCount(const StringView& stringView, TextDirection direction, bool& isAfterExpansion)
</del><ins>+std::pair<unsigned, bool> FontCascade::expansionOpportunityCount(const StringView& stringView, TextDirection direction, ExpansionBehavior expansionBehavior)
</ins><span class="cx"> {
</span><ins>+ // For each character, iterating from left to right:
+ // If it is recognized as a space, insert an opportunity after it
+ // If it is an ideograph, insert one opportunity before it and one opportunity after it
+ // Do this such a way so that there are not two opportunities next to each other.
</ins><span class="cx"> if (stringView.is8Bit())
</span><del>- return expansionOpportunityCountInternal(stringView.characters8(), stringView.length(), direction, isAfterExpansion);
- return expansionOpportunityCountInternal(stringView.characters16(), stringView.length(), direction, isAfterExpansion);
</del><ins>+ return expansionOpportunityCountInternal(stringView.characters8(), stringView.length(), direction, expansionBehavior);
+ return expansionOpportunityCountInternal(stringView.characters16(), stringView.length(), direction, expansionBehavior);
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+bool FontCascade::leadingExpansionOpportunity(const StringView& stringView, TextDirection direction)
+{
+ if (!stringView.length())
+ return false;
+
+ UChar32 initialCharacter;
+ if (direction == LTR) {
+ initialCharacter = stringView[0];
+ if (!stringView.is8Bit() && U16_IS_LEAD(initialCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1]))
+ initialCharacter = U16_GET_SUPPLEMENTARY(initialCharacter, stringView[1]);
+ } else {
+ initialCharacter = stringView[stringView.length() - 1];
+ if (!stringView.is8Bit() && U16_IS_TRAIL(initialCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2]))
+ initialCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], initialCharacter);
+ }
+
+ return canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(initialCharacter);
+}
+
+bool FontCascade::trailingExpansionOpportunity(const StringView& stringView, TextDirection direction)
+{
+ if (!stringView.length())
+ return false;
+
+ UChar32 finalCharacter;
+ if (direction == LTR) {
+ finalCharacter = stringView[stringView.length() - 1];
+ if (!stringView.is8Bit() && U16_IS_TRAIL(finalCharacter) && stringView.length() > 1 && U16_IS_LEAD(stringView[stringView.length() - 2]))
+ finalCharacter = U16_GET_SUPPLEMENTARY(stringView[stringView.length() - 2], finalCharacter);
+ } else {
+ finalCharacter = stringView[0];
+ if (!stringView.is8Bit() && U16_IS_LEAD(finalCharacter) && stringView.length() > 1 && U16_IS_TRAIL(stringView[1]))
+ finalCharacter = U16_GET_SUPPLEMENTARY(finalCharacter, stringView[1]);
+ }
+
+ return treatAsSpace(finalCharacter) || (canExpandAroundIdeographsInComplexText() && isCJKIdeographOrSymbol(finalCharacter));
+}
+
</ins><span class="cx"> bool FontCascade::canReceiveTextEmphasis(UChar32 c)
</span><span class="cx"> {
</span><span class="cx"> if (U_GET_GC_MASK(c) & (U_GC_Z_MASK | U_GC_CN_MASK | U_GC_CC_MASK | U_GC_CF_MASK))
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsFontCascadeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/FontCascade.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/FontCascade.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/FontCascade.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -194,11 +194,17 @@
</span><span class="cx"> static bool isCJKIdeograph(UChar32);
</span><span class="cx"> static bool isCJKIdeographOrSymbol(UChar32);
</span><span class="cx">
</span><del>- // BEWARE: If isAfterExpansion is true after this function call, then the returned value includes a trailing opportunity
- // which may or may not actually be present. RenderBlockFlow::computeInlineDirectionPositionsForSegment() compensates
- // for this by decrementing the returned value if isAfterExpansion is true at the end of a line.
- static unsigned expansionOpportunityCount(const StringView&, TextDirection, bool& isAfterExpansion);
</del><ins>+ // Returns (the number of opportunities, whether the last expansion is a trailing expansion)
+ // If there are no opportunities, the bool will be true iff we are forbidding leading expansions.
+ static std::pair<unsigned, bool> expansionOpportunityCount(const StringView&, TextDirection, ExpansionBehavior);
</ins><span class="cx">
</span><ins>+ // Whether or not there is an expansion opportunity just before the first character
+ // Note that this does not take a isAfterExpansion flag; this assumes that isAfterExpansion is false
+ // Here, "Leading" and "Trailing" are relevant after the line has been rearranged for bidi.
+ // ("Leading" means "left" and "Trailing" means "right.")
+ static bool leadingExpansionOpportunity(const StringView&, TextDirection);
+ static bool trailingExpansionOpportunity(const StringView&, TextDirection);
+
</ins><span class="cx"> WEBCORE_EXPORT static void setShouldUseSmoothing(bool);
</span><span class="cx"> WEBCORE_EXPORT static bool shouldUseSmoothing();
</span><span class="cx">
</span><span class="lines">@@ -237,8 +243,8 @@
</span><span class="cx"> int offsetForPositionForComplexText(const TextRun&, float position, bool includePartialGlyphs) const;
</span><span class="cx"> void adjustSelectionRectForComplexText(const TextRun&, LayoutRect& selectionRect, int from, int to) const;
</span><span class="cx">
</span><del>- static unsigned expansionOpportunityCountInternal(const LChar*, size_t length, TextDirection, bool& isAfterExpansion);
- static unsigned expansionOpportunityCountInternal(const UChar*, size_t length, TextDirection, bool& isAfterExpansion);
</del><ins>+ static std::pair<unsigned, bool> expansionOpportunityCountInternal(const LChar*, size_t length, TextDirection, ExpansionBehavior);
+ static std::pair<unsigned, bool> expansionOpportunityCountInternal(const UChar*, size_t length, TextDirection, ExpansionBehavior);
</ins><span class="cx">
</span><span class="cx"> friend struct WidthIterator;
</span><span class="cx"> friend class SVGTextRunRenderingContext;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsGlyphBufferh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/GlyphBuffer.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/GlyphBuffer.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/GlyphBuffer.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -100,6 +100,9 @@
</span><span class="cx">
</span><span class="cx"> void setInitialAdvance(GlyphBufferAdvance initialAdvance) { m_initialAdvance = initialAdvance; }
</span><span class="cx"> const GlyphBufferAdvance& initialAdvance() const { return m_initialAdvance; }
</span><ins>+
+ void setLeadingExpansion(float leadingExpansion) { m_leadingExpansion = leadingExpansion; }
+ float leadingExpansion() const { return m_leadingExpansion; }
</ins><span class="cx">
</span><span class="cx"> Glyph glyphAt(int index) const
</span><span class="cx"> {
</span><span class="lines">@@ -244,6 +247,7 @@
</span><span class="cx"> #if PLATFORM(WIN)
</span><span class="cx"> Vector<FloatSize, 2048> m_offsets;
</span><span class="cx"> #endif
</span><ins>+ float m_leadingExpansion;
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsTextRunh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/TextRun.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/TextRun.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/TextRun.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -165,8 +165,7 @@
</span><span class="cx"> float xPos() const { return m_xpos; }
</span><span class="cx"> void setXPos(float xPos) { m_xpos = xPos; }
</span><span class="cx"> float expansion() const { return m_expansion; }
</span><del>- bool allowsLeadingExpansion() const { return m_expansionBehavior & AllowLeadingExpansion; }
- bool allowsTrailingExpansion() const { return m_expansionBehavior & AllowTrailingExpansion; }
</del><ins>+ ExpansionBehavior expansionBehavior() const { return m_expansionBehavior; }
</ins><span class="cx"> TextDirection direction() const { return static_cast<TextDirection>(m_direction); }
</span><span class="cx"> bool rtl() const { return m_direction == RTL; }
</span><span class="cx"> bool ltr() const { return m_direction == LTR; }
</span><span class="lines">@@ -219,7 +218,7 @@
</span><span class="cx"> float m_horizontalGlyphStretch;
</span><span class="cx">
</span><span class="cx"> float m_expansion;
</span><del>- ExpansionBehavior m_expansionBehavior : 2;
</del><ins>+ unsigned m_expansionBehavior : 4;
</ins><span class="cx"> unsigned m_allowTabs : 1;
</span><span class="cx"> unsigned m_direction : 1;
</span><span class="cx"> unsigned m_directionalOverride : 1; // Was this direction set by an override character.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsWidthIteratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/WidthIterator.cpp (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/WidthIterator.cpp        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/WidthIterator.cpp        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -39,7 +39,7 @@
</span><span class="cx"> , m_run(run)
</span><span class="cx"> , m_currentCharacter(0)
</span><span class="cx"> , m_runWidthSoFar(0)
</span><del>- , m_isAfterExpansion(!run.allowsLeadingExpansion())
</del><ins>+ , m_isAfterExpansion((run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion)
</ins><span class="cx"> , m_finalRoundingWidth(0)
</span><span class="cx"> , m_typesettingFeatures(font->typesettingFeatures())
</span><span class="cx"> , m_fallbackFonts(fallbackFonts)
</span><span class="lines">@@ -56,10 +56,7 @@
</span><span class="cx"> if (!m_expansion)
</span><span class="cx"> m_expansionPerOpportunity = 0;
</span><span class="cx"> else {
</span><del>- bool isAfterExpansion = m_isAfterExpansion;
- unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
- if (isAfterExpansion && !m_run.allowsTrailingExpansion())
- expansionOpportunityCount--;
</del><ins>+ unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, run.expansionBehavior()).first;
</ins><span class="cx">
</span><span class="cx"> if (!expansionOpportunityCount)
</span><span class="cx"> m_expansionPerOpportunity = 0;
</span><span class="lines">@@ -168,13 +165,47 @@
</span><span class="cx"> return widthDifference;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
+{
+ bool expandLeft = ideograph;
+ bool expandRight = ideograph;
+ if (treatAsSpace) {
+ if (ltr)
+ expandRight = true;
+ else
+ expandLeft = true;
+ }
+ if (isAfterExpansion) {
+ if (ltr)
+ expandLeft = false;
+ else
+ expandRight = false;
+ }
+ ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
+ ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
+ if (forbidLeadingExpansion)
+ expandLeft = false;
+ if (forbidTrailingExpansion)
+ expandRight = false;
+ if (forceLeadingExpansion)
+ expandLeft = true;
+ if (forceTrailingExpansion)
+ expandRight = true;
+ return std::make_pair(expandLeft, expandRight);
+}
+
</ins><span class="cx"> template <typename TextIterator>
</span><span class="cx"> inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
</span><span class="cx"> {
</span><span class="cx"> bool rtl = m_run.rtl();
</span><span class="cx"> bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
</span><span class="cx">
</span><ins>+ bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
+ bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
+ bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
</ins><span class="cx"> float widthSinceLastRounding = m_runWidthSoFar;
</span><ins>+ float leftoverJustificationWidth = 0;
</ins><span class="cx"> m_runWidthSoFar = floorf(m_runWidthSoFar);
</span><span class="cx"> widthSinceLastRounding -= m_runWidthSoFar;
</span><span class="cx">
</span><span class="lines">@@ -189,6 +220,7 @@
</span><span class="cx"> unsigned clusterLength = 0;
</span><span class="cx"> CharactersTreatedAsSpace charactersTreatedAsSpace;
</span><span class="cx"> String normalizedSpacesStringCache;
</span><ins>+ // We are iterating in string order, not glyph order. Compare this to ComplexTextController::adjustGlyphsAndAdvances()
</ins><span class="cx"> while (textIterator.consume(character, clusterLength)) {
</span><span class="cx"> unsigned advanceLength = clusterLength;
</span><span class="cx"> int currentCharacter = textIterator.currentCharacter();
</span><span class="lines">@@ -242,36 +274,63 @@
</span><span class="cx">
</span><span class="cx"> if (hasExtraSpacing) {
</span><span class="cx"> // Account for letter-spacing.
</span><del>- if (width && m_font->letterSpacing())
</del><ins>+ if (width) {
</ins><span class="cx"> width += m_font->letterSpacing();
</span><ins>+ width += leftoverJustificationWidth;
+ leftoverJustificationWidth = 0;
+ }
</ins><span class="cx">
</span><span class="cx"> static bool expandAroundIdeographs = FontCascade::canExpandAroundIdeographsInComplexText();
</span><span class="cx"> bool treatAsSpace = FontCascade::treatAsSpace(character);
</span><del>- if (treatAsSpace || (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character))) {
</del><ins>+ bool currentIsLastCharacter = currentCharacter + advanceLength == static_cast<size_t>(m_run.length());
+ bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
+ bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
+ bool forbidLeadingExpansion = false;
+ bool forbidTrailingExpansion = false;
+ if (runForcesLeadingExpansion)
+ forceLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
+ if (runForcesTrailingExpansion)
+ forceTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
+ if (runForbidsLeadingExpansion)
+ forbidLeadingExpansion = m_run.ltr() ? !currentCharacter : currentIsLastCharacter;
+ if (runForbidsTrailingExpansion)
+ forbidTrailingExpansion = m_run.ltr() ? currentIsLastCharacter : !currentCharacter;
+ bool ideograph = (expandAroundIdeographs && FontCascade::isCJKIdeographOrSymbol(character));
+ if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
</ins><span class="cx"> // Distribute the run's total expansion evenly over all expansion opportunities in the run.
</span><span class="cx"> if (m_expansion) {
</span><ins>+ bool expandLeft, expandRight;
+ std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), m_isAfterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
</ins><span class="cx"> float previousExpansion = m_expansion;
</span><del>- if (!treatAsSpace && !m_isAfterExpansion) {
- // Take the expansion opportunity before this ideograph.
- m_expansion -= m_expansionPerOpportunity;
- float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
- m_runWidthSoFar += expansionAtThisOpportunity;
- if (glyphBuffer) {
- if (glyphBuffer->isEmpty()) {
- if (m_forTextEmphasis)
- glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
- else
- glyphBuffer->add(font->spaceGlyph(), font, expansionAtThisOpportunity, currentCharacter);
- } else
- glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
</del><ins>+ if (expandLeft) {
+ if (m_run.ltr()) {
+ // Increase previous width
+ m_expansion -= m_expansionPerOpportunity;
+ float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
+ m_runWidthSoFar += expansionAtThisOpportunity;
+ if (glyphBuffer) {
+ if (glyphBuffer->isEmpty()) {
+ if (m_forTextEmphasis)
+ glyphBuffer->add(font->zeroWidthSpaceGlyph(), font, m_expansionPerOpportunity, currentCharacter);
+ else
+ glyphBuffer->add(font->spaceGlyph(), font, expansionAtThisOpportunity, currentCharacter);
+ } else
+ glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
+ }
+ } else {
+ // Increase next width
+ float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion - m_expansionPerOpportunity);
+ leftoverJustificationWidth += expansionAtThisOpportunity;
+ m_isAfterExpansion = true;
</ins><span class="cx"> }
</span><span class="cx"> previousExpansion = m_expansion;
</span><span class="cx"> }
</span><del>- if (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + advanceLength < static_cast<size_t>(m_run.length()))
- || (m_run.rtl() && currentCharacter)) {
</del><ins>+ if (expandRight) {
</ins><span class="cx"> m_expansion -= m_expansionPerOpportunity;
</span><del>- width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
- m_isAfterExpansion = true;
</del><ins>+ float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
+ width += expansionAtThisOpportunity;
+ if (m_run.ltr())
+ m_isAfterExpansion = true;
</ins><span class="cx"> }
</span><span class="cx"> } else
</span><span class="cx"> m_isAfterExpansion = false;
</span><span class="lines">@@ -341,6 +400,13 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (leftoverJustificationWidth) {
+ if (m_forTextEmphasis)
+ glyphBuffer->add(lastFontData->zeroWidthSpaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length());
+ else
+ glyphBuffer->add(lastFontData->spaceGlyph(), lastFontData, leftoverJustificationWidth, m_run.length());
+ }
+
</ins><span class="cx"> if (shouldApplyFontTransforms()) {
</span><span class="cx"> m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace);
</span><span class="cx"> if (glyphBuffer)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicscocoaFontCascadeCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/cocoa/FontCascadeCocoa.mm        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -602,7 +602,7 @@
</span><span class="cx"> float afterWidth = controller.runWidthSoFar();
</span><span class="cx">
</span><span class="cx"> if (run.rtl())
</span><del>- selectionRect.move(controller.totalWidth() - afterWidth, 0);
</del><ins>+ selectionRect.move(controller.totalWidth() - afterWidth + controller.leadingExpansion(), 0);
</ins><span class="cx"> else
</span><span class="cx"> selectionRect.move(beforeWidth, 0);
</span><span class="cx"> selectionRect.setWidth(afterWidth - beforeWidth);
</span><span class="lines">@@ -623,7 +623,7 @@
</span><span class="cx"> float afterWidth = controller.runWidthSoFar();
</span><span class="cx">
</span><span class="cx"> if (run.rtl()) {
</span><del>- initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth;
</del><ins>+ initialAdvance = controller.totalWidth() + controller.finalRoundingWidth() - afterWidth + controller.leadingExpansion();
</ins><span class="cx"> glyphBuffer.reverse(0, glyphBuffer.size());
</span><span class="cx"> } else
</span><span class="cx"> initialAdvance = beforeWidth;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsmacComplexTextControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -133,7 +133,6 @@
</span><span class="cx"> , m_finalRoundingWidth(0)
</span><span class="cx"> , m_expansion(run.expansion())
</span><span class="cx"> , m_leadingExpansion(0)
</span><del>- , m_afterExpansion(!run.allowsLeadingExpansion())
</del><span class="cx"> , m_fallbackFonts(fallbackFonts)
</span><span class="cx"> , m_minGlyphBoundingBoxX(std::numeric_limits<float>::max())
</span><span class="cx"> , m_maxGlyphBoundingBoxX(std::numeric_limits<float>::min())
</span><span class="lines">@@ -144,10 +143,7 @@
</span><span class="cx"> if (!m_expansion)
</span><span class="cx"> m_expansionPerOpportunity = 0;
</span><span class="cx"> else {
</span><del>- bool isAfterExpansion = m_afterExpansion;
- unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
- if (isAfterExpansion && !m_run.allowsTrailingExpansion())
- expansionOpportunityCount--;
</del><ins>+ unsigned expansionOpportunityCount = FontCascade::expansionOpportunityCount(m_run.text(), m_run.ltr() ? LTR : RTL, run.expansionBehavior()).first;
</ins><span class="cx">
</span><span class="cx"> if (!expansionOpportunityCount)
</span><span class="cx"> m_expansionPerOpportunity = 0;
</span><span class="lines">@@ -519,8 +515,10 @@
</span><span class="cx"> // We must store the initial advance for the first glyph we are going to draw.
</span><span class="cx"> // When leftmostGlyph is 0, it represents the first glyph to draw, taking into
</span><span class="cx"> // account the text direction.
</span><del>- if (glyphBuffer && !leftmostGlyph)
</del><ins>+ if (glyphBuffer && !leftmostGlyph) {
</ins><span class="cx"> glyphBuffer->setInitialAdvance(complexTextRun.initialAdvance());
</span><ins>+ glyphBuffer->setLeadingExpansion(m_leadingExpansion);
+ }
</ins><span class="cx">
</span><span class="cx"> while (m_glyphInCurrentRun < glyphCount) {
</span><span class="cx"> unsigned glyphStartOffset = complexTextRun.indexAt(g);
</span><span class="lines">@@ -577,11 +575,42 @@
</span><span class="cx"> m_runWidthSoFar += m_finalRoundingWidth;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static inline std::pair<bool, bool> expansionLocation(bool ideograph, bool treatAsSpace, bool ltr, bool isAfterExpansion, bool forbidLeadingExpansion, bool forbidTrailingExpansion, bool forceLeadingExpansion, bool forceTrailingExpansion)
+{
+ bool expandLeft = ideograph;
+ bool expandRight = ideograph;
+ if (treatAsSpace) {
+ if (ltr)
+ expandRight = true;
+ else
+ expandLeft = true;
+ }
+ if (isAfterExpansion)
+ expandLeft = false;
+ ASSERT(!forbidLeadingExpansion || !forceLeadingExpansion);
+ ASSERT(!forbidTrailingExpansion || !forceTrailingExpansion);
+ if (forbidLeadingExpansion)
+ expandLeft = false;
+ if (forbidTrailingExpansion)
+ expandRight = false;
+ if (forceLeadingExpansion)
+ expandLeft = true;
+ if (forceTrailingExpansion)
+ expandRight = true;
+ return std::make_pair(expandLeft, expandRight);
+}
+
</ins><span class="cx"> void ComplexTextController::adjustGlyphsAndAdvances()
</span><span class="cx"> {
</span><ins>+ bool afterExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
</ins><span class="cx"> CGFloat widthSinceLastCommit = 0;
</span><span class="cx"> size_t runCount = m_complexTextRuns.size();
</span><span class="cx"> bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled();
</span><ins>+ bool runForcesLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForceLeadingExpansion;
+ bool runForcesTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForceTrailingExpansion;
+ bool runForbidsLeadingExpansion = (m_run.expansionBehavior() & LeadingExpansionMask) == ForbidLeadingExpansion;
+ bool runForbidsTrailingExpansion = (m_run.expansionBehavior() & TrailingExpansionMask) == ForbidTrailingExpansion;
+ // We are iterating in glyph order, not string order. Compare this to WidthIterator::advanceInternal()
</ins><span class="cx"> for (size_t r = 0; r < runCount; ++r) {
</span><span class="cx"> ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
</span><span class="cx"> unsigned glyphCount = complexTextRun.glyphCount();
</span><span class="lines">@@ -662,16 +691,33 @@
</span><span class="cx"> if (hasExtraSpacing) {
</span><span class="cx"> // If we're a glyph with an advance, go ahead and add in letter-spacing.
</span><span class="cx"> // That way we weed out zero width lurkers. This behavior matches the fast text code path.
</span><del>- if (advance.width && m_font.letterSpacing())
</del><ins>+ if (advance.width)
</ins><span class="cx"> advance.width += m_font.letterSpacing();
</span><span class="cx">
</span><ins>+ bool lastCharacter = static_cast<unsigned>(characterIndex + 1) == m_run.length() || (U16_IS_SURROGATE_LEAD(ch) && static_cast<unsigned>(characterIndex + 2) == m_run.length() && U16_IS_SURROGATE_TRAIL(*(cp + characterIndex + 1)));
+
+ bool forceLeadingExpansion = false; // On the left, regardless of m_run.ltr()
+ bool forceTrailingExpansion = false; // On the right, regardless of m_run.ltr()
+ bool forbidLeadingExpansion = false;
+ bool forbidTrailingExpansion = false;
+ if (runForcesLeadingExpansion)
+ forceLeadingExpansion = m_run.ltr() ? !characterIndex : lastCharacter;
+ if (runForcesTrailingExpansion)
+ forceTrailingExpansion = m_run.ltr() ? lastCharacter : !characterIndex;
+ if (runForbidsLeadingExpansion)
+ forbidLeadingExpansion = m_run.ltr() ? !characterIndex : lastCharacter;
+ if (runForbidsTrailingExpansion)
+ forbidTrailingExpansion = m_run.ltr() ? lastCharacter : !characterIndex;
</ins><span class="cx"> // Handle justification and word-spacing.
</span><del>- if (treatAsSpace || FontCascade::isCJKIdeographOrSymbol(ch)) {
</del><ins>+ bool ideograph = FontCascade::isCJKIdeographOrSymbol(ch);
+ if (treatAsSpace || ideograph || forceLeadingExpansion || forceTrailingExpansion) {
</ins><span class="cx"> // Distribute the run's total expansion evenly over all expansion opportunities in the run.
</span><span class="cx"> if (m_expansion) {
</span><ins>+ bool expandLeft, expandRight;
+ std::tie(expandLeft, expandRight) = expansionLocation(ideograph, treatAsSpace, m_run.ltr(), afterExpansion, forbidLeadingExpansion, forbidTrailingExpansion, forceLeadingExpansion, forceTrailingExpansion);
</ins><span class="cx"> float previousExpansion = m_expansion;
</span><del>- if (!treatAsSpace && !m_afterExpansion) {
- // Take the expansion opportunity before this ideograph.
</del><ins>+ if (expandLeft) {
+ // Increase previous width
</ins><span class="cx"> m_expansion -= m_expansionPerOpportunity;
</span><span class="cx"> float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
</span><span class="cx"> m_totalWidth += expansionAtThisOpportunity;
</span><span class="lines">@@ -681,19 +727,20 @@
</span><span class="cx"> m_adjustedAdvances.last().width += expansionAtThisOpportunity;
</span><span class="cx"> previousExpansion = m_expansion;
</span><span class="cx"> }
</span><del>- if (!lastGlyph || m_run.allowsTrailingExpansion()) {
</del><ins>+ if (expandRight) {
</ins><span class="cx"> m_expansion -= m_expansionPerOpportunity;
</span><del>- advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
- m_afterExpansion = true;
</del><ins>+ float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
+ advance.width += expansionAtThisOpportunity;
+ afterExpansion = true;
</ins><span class="cx"> }
</span><span class="cx"> } else
</span><del>- m_afterExpansion = false;
</del><ins>+ afterExpansion = false;
</ins><span class="cx">
</span><span class="cx"> // Account for word-spacing.
</span><span class="cx"> if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.wordSpacing())
</span><span class="cx"> advance.width += m_font.wordSpacing();
</span><span class="cx"> } else
</span><del>- m_afterExpansion = false;
</del><ins>+ afterExpansion = false;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> // Apply rounding hacks if needed.
</span><span class="lines">@@ -746,6 +793,7 @@
</span><span class="cx"> if (!isMonotonic)
</span><span class="cx"> complexTextRun.setIsNonMonotonic();
</span><span class="cx"> }
</span><ins>+
</ins><span class="cx"> m_totalWidth += widthSinceLastCommit;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsmacComplexTextControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/graphics/mac/ComplexTextController.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -69,6 +69,8 @@
</span><span class="cx"> float maxGlyphBoundingBoxX() const { return m_maxGlyphBoundingBoxX; }
</span><span class="cx"> float minGlyphBoundingBoxY() const { return m_minGlyphBoundingBoxY; }
</span><span class="cx"> float maxGlyphBoundingBoxY() const { return m_maxGlyphBoundingBoxY; }
</span><ins>+
+ float leadingExpansion() const { return m_leadingExpansion; }
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> class ComplexTextRun : public RefCounted<ComplexTextRun> {
</span><span class="lines">@@ -167,7 +169,6 @@
</span><span class="cx"> float m_expansion;
</span><span class="cx"> float m_expansionPerOpportunity;
</span><span class="cx"> float m_leadingExpansion;
</span><del>- bool m_afterExpansion;
</del><span class="cx">
</span><span class="cx"> HashSet<const Font*>* m_fallbackFonts;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformtextTextFlagsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/text/TextFlags.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/text/TextFlags.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/platform/text/TextFlags.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -32,11 +32,18 @@
</span><span class="cx">
</span><span class="cx"> inline bool isLeftToRightDirection(TextDirection direction) { return direction == LTR; }
</span><span class="cx">
</span><ins>+// Here, "Leading" and "Trailing" are relevant after the line has been rearranged for bidi.
+// ("Leading" means "left" and "Trailing" means "right.")
</ins><span class="cx"> enum ExpansionBehaviorFlags {
</span><span class="cx"> ForbidTrailingExpansion = 0 << 0,
</span><span class="cx"> AllowTrailingExpansion = 1 << 0,
</span><del>- ForbidLeadingExpansion = 0 << 1,
- AllowLeadingExpansion = 1 << 1,
</del><ins>+ ForceTrailingExpansion = 2 << 0,
+ TrailingExpansionMask = 3 << 0,
+
+ ForbidLeadingExpansion = 0 << 2,
+ AllowLeadingExpansion = 1 << 2,
+ ForceLeadingExpansion = 2 << 2,
+ LeadingExpansionMask = 3 << 2,
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> typedef unsigned ExpansionBehavior;
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingInlineBoxh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/InlineBox.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/InlineBox.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/rendering/InlineBox.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -315,10 +315,13 @@
</span><span class="cx"> , m_isHorizontal(isHorizontal)
</span><span class="cx"> , m_endsWithBreak(false)
</span><span class="cx"> , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false)
</span><ins>+ , m_canHaveTrailingExpansion(false)
</ins><span class="cx"> , m_knownToHaveNoOverflow(true)
</span><span class="cx"> , m_hasEllipsisBoxOrHyphen(false)
</span><span class="cx"> , m_dirOverride(false)
</span><span class="cx"> , m_behavesLikeText(false)
</span><ins>+ , m_forceTrailingExpansion(false)
+ , m_forceLeadingExpansion(false)
</ins><span class="cx"> , m_determinedIfNextOnLineExists(false)
</span><span class="cx"> , m_nextOnLineExists(false)
</span><span class="cx"> {
</span><span class="lines">@@ -344,11 +347,14 @@
</span><span class="cx"> ADD_BOOLEAN_BITFIELD(endsWithBreak, EndsWithBreak); // Whether the line ends with a <br>.
</span><span class="cx"> // shared between RootInlineBox and InlineTextBox
</span><span class="cx"> ADD_BOOLEAN_BITFIELD(hasSelectedChildrenOrCanHaveLeadingExpansion, HasSelectedChildrenOrCanHaveLeadingExpansion);
</span><ins>+ ADD_BOOLEAN_BITFIELD(canHaveTrailingExpansion, CanHaveTrailingExpansion);
</ins><span class="cx"> ADD_BOOLEAN_BITFIELD(knownToHaveNoOverflow, KnownToHaveNoOverflow);
</span><span class="cx"> ADD_BOOLEAN_BITFIELD(hasEllipsisBoxOrHyphen, HasEllipsisBoxOrHyphen);
</span><span class="cx"> // for InlineTextBox
</span><span class="cx"> ADD_BOOLEAN_BITFIELD(dirOverride, DirOverride);
</span><span class="cx"> ADD_BOOLEAN_BITFIELD(behavesLikeText, BehavesLikeText); // Whether or not this object represents text with a non-zero height. Includes non-image list markers, text boxes, br.
</span><ins>+ ADD_BOOLEAN_BITFIELD(forceTrailingExpansion, ForceTrailingExpansion);
+ ADD_BOOLEAN_BITFIELD(forceLeadingExpansion, ForceLeadingExpansion);
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> mutable unsigned m_determinedIfNextOnLineExists : 1;
</span><span class="lines">@@ -414,6 +420,12 @@
</span><span class="cx"> void setHasHyphen(bool hasHyphen) { m_bitfields.setHasEllipsisBoxOrHyphen(hasHyphen); }
</span><span class="cx"> bool canHaveLeadingExpansion() const { return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); }
</span><span class="cx"> void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion(canHaveLeadingExpansion); }
</span><ins>+ bool canHaveTrailingExpansion() const { return m_bitfields.canHaveTrailingExpansion(); }
+ void setCanHaveTrailingExpansion(bool canHaveTrailingExpansion) { m_bitfields.setCanHaveTrailingExpansion(canHaveTrailingExpansion); }
+ void setForceTrailingExpansion() { m_bitfields.setForceTrailingExpansion(true); }
+ bool forceTrailingExpansion() const { return m_bitfields.forceTrailingExpansion(); }
+ void setForceLeadingExpansion() { m_bitfields.setForceLeadingExpansion(true); }
+ bool forceLeadingExpansion() const { return m_bitfields.forceLeadingExpansion(); }
</ins><span class="cx">
</span><span class="cx"> // For InlineFlowBox and InlineTextBox
</span><span class="cx"> bool extracted() const { return m_bitfields.extracted(); }
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingInlineTextBoxh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/InlineTextBox.h (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/InlineTextBox.h        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/rendering/InlineTextBox.h        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -77,6 +77,12 @@
</span><span class="cx"> using InlineBox::setHasHyphen;
</span><span class="cx"> using InlineBox::canHaveLeadingExpansion;
</span><span class="cx"> using InlineBox::setCanHaveLeadingExpansion;
</span><ins>+ using InlineBox::canHaveTrailingExpansion;
+ using InlineBox::setCanHaveTrailingExpansion;
+ using InlineBox::forceTrailingExpansion;
+ using InlineBox::setForceTrailingExpansion;
+ using InlineBox::forceLeadingExpansion;
+ using InlineBox::setForceLeadingExpansion;
</ins><span class="cx">
</span><span class="cx"> static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderBlockLineLayoutcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp (182235 => 182236)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp        2015-04-01 15:56:34 UTC (rev 182235)
+++ trunk/Source/WebCore/rendering/RenderBlockLineLayout.cpp        2015-04-01 16:58:20 UTC (rev 182236)
</span><span class="lines">@@ -722,6 +722,45 @@
</span><span class="cx"> lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static inline ExpansionBehavior expansionBehaviorForInlineTextBox(bool isAfterExpansion)
+{
+ ExpansionBehavior result = AllowTrailingExpansion;
+ result |= isAfterExpansion ? ForbidLeadingExpansion : AllowLeadingExpansion;
+ return result;
+}
+
+static inline void applyExpansionBehavior(InlineTextBox& textBox, ExpansionBehavior expansionBehavior)
+{
+ switch (expansionBehavior & LeadingExpansionMask) {
+ case ForceLeadingExpansion:
+ textBox.setForceLeadingExpansion();
+ break;
+ case ForbidLeadingExpansion:
+ textBox.setCanHaveLeadingExpansion(false);
+ break;
+ case AllowLeadingExpansion:
+ textBox.setCanHaveLeadingExpansion(true);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ switch (expansionBehavior & TrailingExpansionMask) {
+ case ForceTrailingExpansion:
+ textBox.setForceTrailingExpansion();
+ break;
+ case ForbidTrailingExpansion:
+ textBox.setCanHaveTrailingExpansion(false);
+ break;
+ case AllowTrailingExpansion:
+ textBox.setCanHaveTrailingExpansion(true);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
</ins><span class="cx"> BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
</span><span class="cx"> float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
</span><span class="cx"> WordMeasurements& wordMeasurements)
</span><span class="lines">@@ -742,10 +781,12 @@
</span><span class="cx"> }
</span><span class="cx"> if (is<RenderText>(run->renderer())) {
</span><span class="cx"> auto& renderText = downcast<RenderText>(run->renderer());
</span><ins>+ auto& textBox = downcast<InlineTextBox>(*run->box());
</ins><span class="cx"> if (textAlign == JUSTIFY && run != trailingSpaceRun) {
</span><del>- if (!isAfterExpansion)
- downcast<InlineTextBox>(*run->box()).setCanHaveLeadingExpansion(true);
- unsigned opportunitiesInRun = FontCascade::expansionOpportunityCount(renderText.stringView(run->m_start, run->m_stop), run->box()->direction(), isAfterExpansion);
</del><ins>+ ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(isAfterExpansion);
+ applyExpansionBehavior(textBox, expansionBehavior);
+ unsigned opportunitiesInRun;
+ std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(run->m_start, run->m_stop), run->box()->direction(), expansionBehavior);
</ins><span class="cx"> expansionOpportunities.append(opportunitiesInRun);
</span><span class="cx"> expansionOpportunityCount += opportunitiesInRun;
</span><span class="cx"> }
</span><span class="lines">@@ -766,11 +807,13 @@
</span><span class="cx"> for (auto* leafChild = rubyBase->firstRootBox()->firstLeafChild(); leafChild; leafChild = leafChild->nextLeafChild()) {
</span><span class="cx"> if (!is<InlineTextBox>(*leafChild))
</span><span class="cx"> continue;
</span><del>- if (!isAfterExpansion)
- downcast<InlineTextBox>(*leafChild).setCanHaveLeadingExpansion(true);
</del><ins>+ auto& textBox = downcast<InlineTextBox>(*leafChild);
</ins><span class="cx"> encounteredJustifiedRuby = true;
</span><span class="cx"> auto& renderText = downcast<RenderText>(leafChild->renderer());
</span><del>- unsigned opportunitiesInRun = FontCascade::expansionOpportunityCount(renderText.stringView(), leafChild->direction(), isAfterExpansion);
</del><ins>+ ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(isAfterExpansion);
+ applyExpansionBehavior(textBox, expansionBehavior);
+ unsigned opportunitiesInRun;
+ std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(), leafChild->direction(), expansionBehavior);
</ins><span class="cx"> expansionOpportunities.append(opportunitiesInRun);
</span><span class="cx"> expansionOpportunityCount += opportunitiesInRun;
</span><span class="cx"> }
</span></span></pre>
</div>
</div>
</body>
</html>