mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Basic bidi support for SVG. Bug 620446, r=roc, a=roc
This commit is contained in:
parent
7368bb2f8a
commit
3bd533614c
@ -36,6 +36,7 @@ EXPORTS = \
|
||||
gfxTypes.h \
|
||||
gfxTextRunCache.h \
|
||||
gfxTextRunWordCache.h \
|
||||
gfxUnicodeProperties.h \
|
||||
gfxUtils.h \
|
||||
gfxUserFontSet.h \
|
||||
GLDefs.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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -510,6 +510,17 @@ public:
|
||||
*/
|
||||
nsresult SetPara(const PRUnichar *aText, PRInt32 aLength, nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels);
|
||||
|
||||
/**
|
||||
* Get the directionality of the text.
|
||||
*
|
||||
* @param aDirection receives a <code>NSBIDI_XXX</code> 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
|
||||
/**
|
||||
* <code>SetLine</code> sets an <code>nsBidi</code> to
|
||||
@ -546,17 +557,6 @@ public:
|
||||
*/
|
||||
nsresult SetLine(nsIBidi* aParaBidi, PRInt32 aStart, PRInt32 aLimit);
|
||||
|
||||
/**
|
||||
* Get the directionality of the text.
|
||||
*
|
||||
* @param aDirection receives a <code>NSBIDI_XXX</code> 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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -317,6 +317,24 @@ public:
|
||||
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 @@ private:
|
||||
|
||||
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<nsIFrame*> mLogicalFrames;
|
||||
nsTArray<nsIFrame*> mVisualFrames;
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user