diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in
index 632938e11eb4..73ef37c66035 100644
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -36,6 +36,7 @@ EXPORTS = \
gfxTypes.h \
gfxTextRunCache.h \
gfxTextRunWordCache.h \
+ gfxUnicodeProperties.h \
gfxUtils.h \
gfxUserFontSet.h \
GLDefs.h \
diff --git a/gfx/thebes/gfxUnicodeProperties.h b/gfx/thebes/gfxUnicodeProperties.h
index b2574af64040..2224f2e129e7 100644
--- a/gfx/thebes/gfxUnicodeProperties.h
+++ b/gfx/thebes/gfxUnicodeProperties.h
@@ -39,8 +39,9 @@
#define GFX_UNICODEPROPERTIES_H
#include "prtypes.h"
+#include "gfxTypes.h"
-class gfxUnicodeProperties
+class THEBES_API gfxUnicodeProperties
{
public:
static PRUint32 GetMirroredChar(PRUint32 aCh);
diff --git a/layout/base/nsBidi.cpp b/layout/base/nsBidi.cpp
index 175a1a240363..dd99ce9e2b47 100644
--- a/layout/base/nsBidi.cpp
+++ b/layout/base/nsBidi.cpp
@@ -1116,15 +1116,15 @@ void nsBidi::AdjustWSLevels()
}
}
}
-#ifdef FULL_BIDI_ENGINE
-
-/* -------------------------------------------------------------------------- */
nsresult nsBidi::GetDirection(nsBidiDirection* aDirection)
{
*aDirection = mDirection;
return NS_OK;
}
+#ifdef FULL_BIDI_ENGINE
+
+/* -------------------------------------------------------------------------- */
nsresult nsBidi::GetLength(PRInt32* aLength)
{
diff --git a/layout/base/nsBidi.h b/layout/base/nsBidi.h
index f9462aa4b81e..4d2dc4276d36 100644
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -510,6 +510,17 @@ class nsBidi
*/
nsresult SetPara(const PRUnichar *aText, PRInt32 aLength, nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels);
+ /**
+ * Get the directionality of the text.
+ *
+ * @param aDirection receives a NSBIDI_XXX
value that indicates if the entire text
+ * represented by this object is unidirectional,
+ * and which direction, or if it is mixed-directional.
+ *
+ * @see nsBidiDirection
+ */
+ nsresult GetDirection(nsBidiDirection* aDirection);
+
#ifdef FULL_BIDI_ENGINE
/**
* SetLine
sets an nsBidi
to
@@ -546,17 +557,6 @@ class nsBidi
*/
nsresult SetLine(nsIBidi* aParaBidi, PRInt32 aStart, PRInt32 aLimit);
- /**
- * Get the directionality of the text.
- *
- * @param aDirection receives a NSBIDI_XXX
value that indicates if the entire text
- * represented by this object is unidirectional,
- * and which direction, or if it is mixed-directional.
- *
- * @see nsBidiDirection
- */
- nsresult GetDirection(nsBidiDirection* aDirection);
-
/**
* Get the length of the text.
*
diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp
index 3823f017ced0..fd69e29166d6 100644
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -55,6 +55,7 @@
#include "nsPlaceholderFrame.h"
#include "nsContainerFrame.h"
#include "nsFirstLetterFrame.h"
+#include "gfxUnicodeProperties.h"
using namespace mozilla;
@@ -1683,6 +1684,139 @@ nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar*
aMode, aPosResolve, aPosResolveCount, aWidth);
}
+/* static */
+void nsBidiPresUtils::WriteReverse(const PRUnichar* aSrc,
+ PRUint32 aSrcLength,
+ PRUnichar* aDest)
+{
+ const PRUnichar* src = aSrc + aSrcLength;
+ PRUnichar* dest = aDest;
+ PRUint32 UTF32Char;
+
+ while (--src >= aSrc) {
+ if (NS_IS_LOW_SURROGATE(*src)) {
+ if (src > aSrc && NS_IS_HIGH_SURROGATE(*(src - 1))) {
+ UTF32Char = SURROGATE_TO_UCS4(*(src - 1), *src);
+ --src;
+ } else {
+ UTF32Char = UCS2_REPLACEMENT_CHAR;
+ }
+ } else if (NS_IS_HIGH_SURROGATE(*src)) {
+ // paired high surrogates are handled above, so this is a lone high surrogate
+ UTF32Char = UCS2_REPLACEMENT_CHAR;
+ } else {
+ UTF32Char = *src;
+ }
+
+ UTF32Char = gfxUnicodeProperties::GetMirroredChar(UTF32Char);
+
+ if (IS_IN_BMP(UTF32Char)) {
+ *(dest++) = UTF32Char;
+ } else {
+ *(dest++) = H_SURROGATE(UTF32Char);
+ *(dest++) = L_SURROGATE(UTF32Char);
+ }
+ }
+
+ NS_ASSERTION(dest - aDest == aSrcLength, "Whole string not copied");
+}
+
+/* static */
+PRBool nsBidiPresUtils::WriteLogicalToVisual(const PRUnichar* aSrc,
+ PRUint32 aSrcLength,
+ PRUnichar* aDest,
+ nsBidiLevel aBaseDirection,
+ nsBidi* aBidiEngine)
+{
+ const PRUnichar* src = aSrc;
+ nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nsnull);
+ if (NS_FAILED(rv)) {
+ return PR_FALSE;
+ }
+
+ nsBidiDirection dir;
+ rv = aBidiEngine->GetDirection(&dir);
+ // NSBIDI_LTR returned from GetDirection means the whole text is LTR
+ if (NS_FAILED(rv) || dir == NSBIDI_LTR) {
+ return PR_FALSE;
+ }
+
+ PRInt32 runCount;
+ rv = aBidiEngine->CountRuns(&runCount);
+ if (NS_FAILED(rv)) {
+ return PR_FALSE;
+ }
+
+ PRInt32 runIndex, start, length;
+ PRUnichar* dest = aDest;
+
+ for (runIndex = 0; runIndex < runCount; ++runIndex) {
+ rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir);
+ if (NS_FAILED(rv)) {
+ return PR_FALSE;
+ }
+
+ src = aSrc + start;
+
+ if (dir == NSBIDI_RTL) {
+ WriteReverse(src, length, dest);
+ dest += length;
+ } else {
+ do {
+ NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength,
+ "logical index out of range");
+ NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range");
+ *(dest++) = *(src++);
+ } while (--length);
+ }
+ }
+
+ NS_ASSERTION(dest - aDest == aSrcLength, "whole string not copied");
+ return PR_TRUE;
+}
+
+void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource,
+ nsAString& aDest,
+ nsBidiLevel aBaseDirection,
+ PRBool aOverride)
+{
+ aDest.SetLength(0);
+ PRUint32 srcLength = aSource.Length();
+ if (srcLength == 0)
+ return;
+ if (!EnsureStringLength(aDest, srcLength)) {
+ return;
+ }
+ nsAString::const_iterator fromBegin, fromEnd;
+ nsAString::iterator toBegin;
+ aSource.BeginReading(fromBegin);
+ aSource.EndReading(fromEnd);
+ aDest.BeginWriting(toBegin);
+
+ if (aOverride) {
+ if (aBaseDirection == NSBIDI_RTL) {
+ // no need to use the converter -- just copy the string in reverse order
+ WriteReverse(fromBegin.get(), srcLength, toBegin.get());
+ } else {
+ // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
+ // simple copy
+ aDest.SetLength(0);
+ }
+ } else {
+ if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(),
+ aBaseDirection, mBidiEngine)) {
+ aDest.SetLength(0);
+ }
+ }
+
+ if (aDest.IsEmpty()) {
+ // Either there was an error or the source is unidirectional
+ // left-to-right. In either case, just copy source to dest.
+ CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
+ aDest);
+ }
+}
+
PRUint32 nsBidiPresUtils::EstimateMemoryUsed()
{
PRUint32 size = 0;
diff --git a/layout/base/nsBidiPresUtils.h b/layout/base/nsBidiPresUtils.h
index c0b9382a2f44..e8e24c83091e 100644
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -317,6 +317,24 @@ class nsBidiPresUtils {
PRInt32 aPosResolveCount,
nscoord* aWidth);
+ /**
+ * Make a copy of a string, converting from logical to visual order
+ *
+ * @param aSource the source string
+ * @param aDest the destination string
+ * @param aBaseDirection the base direction of the string
+ * (NSBIDI_LTR or NSBIDI_RTL to force the base direction;
+ * NSBIDI_DEFAULT_LTR or NSBIDI_DEFAULT_RTL to let the bidi engine
+ * determine the direction from rules P2 and P3 of the bidi algorithm.
+ * @see nsBidi::GetPara
+ * @param aOverride if TRUE, the text has a bidi override, according to
+ * the direction in aDir
+ */
+ void CopyLogicalToVisual(const nsAString& aSource,
+ nsAString& aDest,
+ nsBidiLevel aBaseDirection,
+ PRBool aOverride);
+
/**
* Guess at how much memory is being used by this nsBidiPresUtils instance,
* including memory used by nsBidi.
@@ -477,6 +495,17 @@ class nsBidiPresUtils {
void StripBidiControlCharacters(PRUnichar* aText,
PRInt32& aTextLength) const;
+
+ static PRBool WriteLogicalToVisual(const PRUnichar* aSrc,
+ PRUint32 aSrcLength,
+ PRUnichar* aDest,
+ nsBidiLevel aBaseDirection,
+ nsBidi* aBidiEngine);
+
+ static void WriteReverse(const PRUnichar* aSrc,
+ PRUint32 aSrcLength,
+ PRUnichar* aDest);
+
nsAutoString mBuffer;
nsTArray mLogicalFrames;
nsTArray mVisualFrames;
diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp
index 88fc2207d481..2bde6669303e 100644
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -39,6 +39,7 @@
#include "nsSVGTextFrame.h"
#include "nsILookAndFeel.h"
#include "nsTextFragment.h"
+#include "nsBidiPresUtils.h"
#include "nsSVGUtils.h"
#include "SVGLengthList.h"
#include "nsIDOMSVGLength.h"
@@ -1548,6 +1549,50 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
if (!GetCharacterData(text))
return PR_FALSE;
+ nsBidiPresUtils* bidiUtils = presContext->GetBidiUtils();
+ if (bidiUtils) {
+ nsAutoString visualText;
+
+ /*
+ * XXXsmontagu: The SVG spec says:
+ *
+ * http://www.w3.org/TR/SVG11/text.html#DirectionProperty
+ * "For the 'direction' property to have any effect, the 'unicode-bidi'
+ * property's value must be embed or bidi-override."
+ *
+ * The SVGTiny spec, on the other hand, says
+ *
+ * http://www.w3.org/TR/SVGTiny12/text.html#DirectionProperty
+ * "For the 'direction' property to have any effect on an element that
+ * does not by itself establish a new text chunk (such as the 'tspan'
+ * element in SVG 1.2 Tiny), the 'unicode-bidi' property's value must
+ * be embed or bidi-override."
+ *
+ * Note that this is different from HTML/CSS, where setting the 'dir'
+ * attribute on an inline element automatically sets unicode-bidi: embed
+ *
+ * Our current implementation of bidi in SVG does not distinguish between
+ * different text elements, but treats every text container frame as a
+ * new text chunk, so we always set the base direction according to the
+ * direction property
+ *
+ * See also XXXsmontagu comments in nsSVGTextFrame::UpdateGlyphPositioning
+ */
+
+ // Get the unicodeBidi property from the parent, because it doesn't
+ // inherit
+ PRBool bidiOverride = (mParent->GetStyleTextReset()->mUnicodeBidi ==
+ NS_STYLE_UNICODE_BIDI_OVERRIDE);
+ nsBidiLevel baseDirection =
+ GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ?
+ NSBIDI_RTL : NSBIDI_LTR;
+ bidiUtils->CopyLogicalToVisual(text, visualText,
+ baseDirection, bidiOverride);
+ if (!visualText.IsEmpty()) {
+ text = visualText;
+ }
+ }
+
gfxMatrix m;
if (aForceGlobalTransform ||
!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp
index ce73a1f8c1ba..7cc6bca2b984 100644
--- a/layout/svg/base/src/nsSVGTextFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextFrame.cpp
@@ -341,6 +341,39 @@ nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
PRUint8 anchor = firstFragment->GetTextAnchor();
+ /**
+ * XXXsmontagu: The SVG spec is very vague as to how 'text-anchor'
+ * interacts with bidirectional text. It says:
+ *
+ * "For scripts that are inherently right to left such as Hebrew and
+ * Arabic [text-anchor: start] is equivalent to right alignment."
+ * and
+ * "For scripts that are inherently right to left such as Hebrew and
+ * Arabic, [text-anchor: end] is equivalent to left alignment.
+ *
+ * It's not clear how this should be implemented in terms of defined
+ * properties, i.e. how one should determine that a particular element
+ * contains a script that is inherently right to left.
+ *
+ * The code below follows http://www.w3.org/TR/SVGTiny12/text.html#TextAnchorProperty
+ * and swaps the values of text-anchor: end and text-anchor: start
+ * whenever the 'direction' property is rtl.
+ *
+ * This is probably the "right" thing to do, but other browsers don't do it,
+ * so I am leaving it inside #if 0 for now for interoperability.
+ *
+ * See also XXXsmontagu comments in nsSVGGlyphFrame::EnsureTextRun
+ */
+#if 0
+ if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
+ if (anchor == NS_STYLE_TEXT_ANCHOR_END) {
+ anchor = NS_STYLE_TEXT_ANCHOR_START;
+ } else if (anchor == NS_STYLE_TEXT_ANCHOR_START) {
+ anchor = NS_STYLE_TEXT_ANCHOR_END;
+ }
+ }
+#endif
+
float chunkLength = 0.0f;
if (anchor != NS_STYLE_TEXT_ANCHOR_START) {
// need to get the total chunk length