Bug 333659. Landing new text interfaces, new gfxPangoTextRun (enabled), new textframe code (disabled). r+sr=dbaron for the part-of-the-build layout parts,r=stuart for the part-of-the-build gfx parts

This commit is contained in:
roc+%cs.cmu.edu 2007-01-16 20:51:52 +00:00
parent a8c339859b
commit e6bad38abe
48 changed files with 11535 additions and 765 deletions

View File

@ -72,6 +72,7 @@ nsCaseTreatment.h \
nsContentCID.h \
nsCopySupport.h \
nsContentCreatorFunctions.h \
nsLineBreaker.h \
nsXMLNameSpaceMap.h \
$(NULL)

View File

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NSLINEBREAKER_H_
#define NSLINEBREAKER_H_
#include "nsString.h"
#include "nsTArray.h"
class nsIAtom;
/**
* A receiver of line break data.
*/
class nsILineBreakSink {
public:
/**
* Sets the break data for a substring of the associated text chunk.
* One or more of these calls will be performed; the union of all substrings
* will cover the entire text chunk. Substrings may overlap (i.e., we may
* set the break-before state of a character more than once).
* @param aBreakBefore the break-before states for the characters in the substring.
*/
virtual void SetBreaks(PRUint32 aStart, PRUint32 aLength, PRPackedBool* aBreakBefore) = 0;
};
/**
* A line-breaking state machine. You feed text into it via AppendText calls
* and it computes the possible line breaks. Because break decisions can
* require a lot of context, the breaks for a piece of text are sometimes not
* known until later text has been seen (or all text ends). So breaks are
* returned via a call to SetBreaks on the nsILineBreakSink object passed
* with each text chunk, which might happen during the corresponding AppendText
* call, or might happen during a later AppendText call or even a Reset()
* call.
*
* The linebreak results MUST NOT depend on how the text is broken up
* into AppendText calls.
*
* The current strategy is that we break the overall text into
* whitespace-delimited "words". Then for words that contain a CJK character,
* we break within the word using JISx4051 rules.
* XXX This approach is not very good and we should replace it with something
* better, such as some variant of UAX#14.
*/
class nsLineBreaker {
public:
nsLineBreaker();
~nsLineBreaker();
// We need finegrained control of the line breaking behaviour to ensure
// that we get tricky CSS semantics right (in particular, the way we currently
// interpret and implement them; there's some ambiguity in the spec). The
// rules for CSS 'white-space' are slightly different for breaks induced by
// whitespace and space induced by nonwhitespace. Breaks induced by
// whitespace are always controlled by the
// 'white-space' property of the text node containing the
// whitespace. Breaks induced by non-whitespace where the break is between
// two nodes are controled by the 'white-space' property on the nearest
// common ancestor node. Therefore we provide separate control over
// a) whether whitespace in this text induces breaks b) whether we can
// break between nonwhitespace inside this text and c) whether we can break
// between nonwhitespace between the last text and this text.
enum {
/**
* Allow breaks before and after whitespace in this block of text
*/
BREAK_WHITESPACE = 0x01,
/**
* Allow breaks between eligible nonwhitespace characters when the break
* is in the interior of this block of text.
*/
BREAK_NONWHITESPACE_INSIDE = 0x02,
/**
* Allow break between eligible nonwhitespace characters when the break
* is at the beginning of this block of text.
*/
BREAK_NONWHITESPACE_BEFORE = 0x04
};
/**
* Feed Unicode text into the linebreaker for analysis.
* If aLength is zero, then we assume the string is "invisible whitespace"
* which can induce breaks.
*/
nsresult AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink);
/**
* Feed 8-bit text into the linebreaker for analysis.
* If aLength is zero, then we assume the string is "invisible whitespace"
* which can induce breaks.
*/
nsresult AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink);
/**
* Reset all state. This means the current run has ended; any outstanding
* calls through nsILineBreakSink are made, and all outstanding references to
* nsILineBreakSink objects are dropped.
* After this call, this linebreaker can be reused.
* This must be called at least once between any call to AppendText() and
* destroying the object.
*/
nsresult Reset() { return FlushCurrentWord(); }
private:
struct TextItem {
TextItem(nsILineBreakSink* aSink, PRUint32 aSinkOffset, PRUint32 aLength,
PRUint32 aFlags)
: mSink(aSink), mSinkOffset(aSinkOffset), mLength(aLength), mFlags(aFlags) {}
nsILineBreakSink* mSink;
PRUint32 mSinkOffset;
PRUint32 mLength;
PRUint32 mFlags;
};
// State for the nonwhitespace "word" that started in previous text and hasn't
// finished yet.
// When the current word ends, this computes the linebreak opportunities
// *inside* the word (excluding either end) and sets them through the
// appropriate sink(s). Then we clear the current word state.
nsresult FlushCurrentWord();
nsAutoTArray<PRUnichar,100> mCurrentWord;
// All the items that contribute to mCurrentWord
nsAutoTArray<TextItem,2> mTextItems;
PRPackedBool mCurrentWordContainsCJK;
// When mCurrentWord is empty, this indicates whether we should allow a break
// before the next text.
PRPackedBool mBreakBeforeNextWord;
};
#endif /*NSLINEBREAKER_H_*/

View File

@ -124,6 +124,7 @@ CPPSRCS = \
nsGkAtoms.cpp \
nsHTMLContentSerializer.cpp \
nsImageLoadingContent.cpp \
nsLineBreaker.cpp \
nsLoadListenerProxy.cpp \
nsMappedAttributes.cpp \
nsNameSpaceManager.cpp \

View File

@ -0,0 +1,313 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsLineBreaker.h"
#include "nsContentUtils.h"
#include "nsILineBreaker.h"
// We only operate on post-transformation text, so we don't see tabs
static inline int
IS_SPACE(PRUnichar u)
{
NS_ASSERTION(u != 0x0009, "Tabs should have been eliminated");
// \r characters are just ignored
return u == 0x0020 || u == 0x000a || u == 0x200b;
}
static inline int
IS_SPACE(PRUint8 u)
{
NS_ASSERTION(u != 0x0009, "Tabs should have been eliminated");
// \r characters are just ignored
return u == 0x0020 || u == 0x000a;
}
static inline int
IS_CJK_CHAR(PRUnichar u)
{
return (0x1100 <= u && u <= 0x11ff) ||
(0x2e80 <= u && u <= 0xd7ff) ||
(0xf900 <= u && u <= 0xfaff) ||
(0xff00 <= u && u <= 0xffef);
}
nsLineBreaker::nsLineBreaker()
: mCurrentWordContainsCJK(PR_FALSE), mBreakBeforeNextWord(PR_FALSE)
{
}
nsLineBreaker::~nsLineBreaker()
{
NS_ASSERTION(mCurrentWord.Length() == 0, "Should have Reset() before destruction!");
}
nsresult
nsLineBreaker::FlushCurrentWord()
{
nsAutoTArray<PRPackedBool,4000> breakState;
if (!breakState.AppendElements(mCurrentWord.Length()))
return NS_ERROR_OUT_OF_MEMORY;
if (!mCurrentWordContainsCJK) {
// Just set everything internal to "no break"!
memset(breakState.Elements(), 0, mCurrentWord.Length());
} else {
nsContentUtils::LineBreaker()->
GetJISx4051Breaks(mCurrentWord.Elements(), mCurrentWord.Length(), breakState.Elements());
}
PRUint32 i;
PRUint32 offset = 0;
for (i = 0; i < mTextItems.Length(); ++i) {
TextItem* ti = &mTextItems[i];
NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
if (!(ti->mFlags & BREAK_NONWHITESPACE_BEFORE) && ti->mSinkOffset == 0) {
breakState[offset] = PR_FALSE;
}
if (!(ti->mFlags & BREAK_NONWHITESPACE_INSIDE)) {
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
memset(breakState.Elements() + offset + exclude, PR_FALSE, ti->mLength - exclude);
}
PRUint32 skipSet = i > 0 ? 1 : 0;
ti->mSink->SetBreaks(ti->mSinkOffset + skipSet, ti->mLength - skipSet,
breakState.Elements() + offset + skipSet);
offset += ti->mLength;
}
mCurrentWord.Clear();
mTextItems.Clear();
mCurrentWordContainsCJK = PR_FALSE;
return NS_OK;
}
nsresult
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink)
{
if (aLength == 0) {
// Treat as "invisible whitespace"
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
mBreakBeforeNextWord |= (aFlags & BREAK_WHITESPACE) != 0;
return NS_OK;
}
PRUint32 offset = 0;
// Continue the current word
if (mCurrentWord.Length() > 0) {
NS_ASSERTION(!mBreakBeforeNextWord, "This should not be set");
while (offset < aLength && !IS_SPACE(aText[offset])) {
mCurrentWord.AppendElement(aText[offset]);
if (!mCurrentWordContainsCJK && IS_CJK_CHAR(aText[offset])) {
mCurrentWordContainsCJK = PR_TRUE;
}
++offset;
}
if (offset > 0) {
mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags));
}
if (offset == aLength)
return NS_OK;
// We encountered whitespace, so we're done with this word
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
}
nsAutoTArray<PRPackedBool,4000> breakState;
if (!breakState.AppendElements(aLength))
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 start = offset;
PRUint32 wordStart = offset;
PRBool wordHasCJK = PR_FALSE;
PRBool breakNext = mBreakBeforeNextWord;
for (;;) {
PRUnichar ch = aText[offset];
PRBool isSpace = IS_SPACE(ch);
breakState[offset] = breakNext;
breakNext = PR_FALSE;
if (isSpace) {
if (offset > wordStart && wordHasCJK) {
if (aFlags & BREAK_NONWHITESPACE_INSIDE) {
// Save current start-of-word state because GetJISx4051Breaks will set it to false
PRPackedBool currentStart = breakState[offset];
nsContentUtils::LineBreaker()->
GetJISx4051Breaks(aText + wordStart, offset - wordStart, breakState.Elements() + offset);
breakState[offset] = currentStart;
}
wordHasCJK = PR_FALSE;
}
if (aFlags & BREAK_WHITESPACE) {
// Allow break either side of the whitespace
breakState[offset] = PR_TRUE;
breakNext = PR_TRUE;
}
++offset;
if (offset >= aLength)
break;
wordStart = offset;
} else {
if (!wordHasCJK && IS_CJK_CHAR(ch)) {
wordHasCJK = PR_TRUE;
}
++offset;
if (offset >= aLength) {
// Save this word
mCurrentWordContainsCJK = wordHasCJK;
PRUint32 len = offset - wordStart;
PRUnichar* elems = mCurrentWord.AppendElements(len);
if (!elems)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(elems, aText + wordStart, sizeof(PRUnichar)*len);
mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags));
// Ensure that the break-before for this word is written out
offset = wordStart + 1;
break;
}
}
}
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
mBreakBeforeNextWord = breakNext;
return NS_OK;
}
nsresult
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
PRUint32 aFlags, nsILineBreakSink* aSink)
{
if (aLength == 0) {
// Treat as "invisible whitespace"
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
mBreakBeforeNextWord |= (aFlags & BREAK_WHITESPACE) != 0;
return NS_OK;
}
PRUint32 offset = 0;
// Continue the current word
if (mCurrentWord.Length() > 0) {
NS_ASSERTION(!mBreakBeforeNextWord, "This should not be set");
while (offset < aLength && !IS_SPACE(aText[offset])) {
mCurrentWord.AppendElement(aText[offset]);
++offset;
}
if (offset > 0) {
mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags));
}
if (offset == aLength) {
// We did not encounter whitespace so the word hasn't finished yet.
return NS_OK;
}
// We encountered whitespace, so we're done with this word
nsresult rv = FlushCurrentWord();
if (NS_FAILED(rv))
return rv;
}
nsAutoTArray<PRPackedBool,4000> breakState;
if (!breakState.AppendElements(aLength))
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 start = offset;
PRUint32 wordStart = offset;
PRBool breakNext = mBreakBeforeNextWord;
for (;;) {
PRUint8 ch = aText[offset];
PRBool isSpace = IS_SPACE(ch);
breakState[offset] = breakNext;
breakNext = PR_FALSE;
if (isSpace) {
if (aFlags & BREAK_WHITESPACE) {
// Allow break either side of the whitespace
breakState[offset] = PR_TRUE;
breakNext = PR_TRUE;
}
++offset;
if (offset >= aLength)
break;
wordStart = offset;
} else {
++offset;
if (offset >= aLength) {
// Save this word
mCurrentWordContainsCJK = PR_FALSE;
PRUint32 len = offset - wordStart;
PRUnichar* elems = mCurrentWord.AppendElements(len);
if (!elems)
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 i;
for (i = wordStart; i < offset; ++i) {
elems[i - wordStart] = aText[i];
}
mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags));
// Ensure that the break-before for this word is written out
offset = wordStart + 1;
break;
}
// We can't break inside words in 8-bit text (no CJK characters), so
// there is no need to do anything else to handle words
}
}
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
mBreakBeforeNextWord = breakNext;
return NS_OK;
}

View File

@ -191,9 +191,11 @@ public:
void SetBidiFlag();
struct FragmentBits {
PRBool mInHeap : 1;
PRBool mIs2b : 1;
PRBool mIsBidi : 1;
// PRPackedBool to ensure that the values are unsigned, because we
// want 0/1, not 0/-1!
PRPackedBool mInHeap : 1;
PRPackedBool mIs2b : 1;
PRPackedBool mIsBidi : 1;
PRUint32 mLength : 29;
};

View File

@ -118,6 +118,8 @@ struct NS_GFX nsFont {
}
PRBool Equals(const nsFont& aOther) const ;
// Compare ignoring differences in 'variant' and 'decoration'
PRBool BaseEquals(const nsFont& aOther) const;
nsFont& operator=(const nsFont& aOther);

View File

@ -93,14 +93,12 @@ nsFont::~nsFont()
{
}
PRBool nsFont::Equals(const nsFont& aOther) const
PRBool nsFont::BaseEquals(const nsFont& aOther) const
{
if ((style == aOther.style) &&
(systemFont == aOther.systemFont) &&
(variant == aOther.variant) &&
(familyNameQuirks == aOther.familyNameQuirks) &&
(weight == aOther.weight) &&
(decorations == aOther.decorations) &&
(size == aOther.size) &&
(sizeAdjust == aOther.sizeAdjust) &&
name.Equals(aOther.name, nsCaseInsensitiveStringComparator())) {
@ -109,6 +107,16 @@ PRBool nsFont::Equals(const nsFont& aOther) const
return PR_FALSE;
}
PRBool nsFont::Equals(const nsFont& aOther) const
{
if (BaseEquals(aOther) &&
(variant == aOther.variant) &&
(decorations == aOther.decorations)) {
return PR_TRUE;
}
return PR_FALSE;
}
nsFont& nsFont::operator=(const nsFont& aOther)
{
name = aOther.name;

View File

@ -124,7 +124,7 @@ CPPSRCS += nsSystemFontsMac.cpp
endif
endif
EXPORTS += nsIThebesRenderingContext.h
EXPORTS += nsIThebesRenderingContext.h nsIThebesFontMetrics.h
LOCAL_INCLUDES = \
-I$(srcdir)/. \

View File

@ -43,6 +43,8 @@
class nsThebesRenderingContext;
class gfxFontGroup;
class nsIThebesFontMetrics : public nsIFontMetrics {
public:
// Get the width for this string. aWidth will be updated with the
@ -110,6 +112,8 @@ public:
virtual PRBool GetRightToLeftText() = 0;
virtual PRInt32 GetMaxStringLength() = 0;
virtual gfxFontGroup* GetThebesFontGroup() = 0;
};
#endif /* __nsIThebesFontMetrics_h */

View File

@ -40,9 +40,10 @@
#include "nsFont.h"
#include "nsString.h"
#include "nsAutoBuffer.h"
#include <stdio.h>
#include "gfxTextRunCache.h"
NS_IMPL_ISUPPORTS1(nsThebesFontMetrics, nsIFontMetrics)
#include <stdlib.h>
@ -55,8 +56,6 @@ NS_IMPL_ISUPPORTS1(nsThebesFontMetrics, nsIFontMetrics)
#include "gfxAtsuiFonts.h"
#endif
#include "gfxTextRunCache.h"
nsThebesFontMetrics::nsThebesFontMetrics()
{
mFontStyle = nsnull;
@ -283,6 +282,43 @@ nsThebesFontMetrics::GetMaxStringLength()
return PR_MAX(1, len);
}
class StubPropertyProvider : public gfxTextRun::PropertyProvider {
public:
StubPropertyProvider(const nscoord* aSpacing = nsnull)
: mSpacing(aSpacing) {}
virtual void ForceRememberText() {
NS_ERROR("This shouldn't be called because we already asked the textrun to remember");
}
virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore) {
NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
}
virtual gfxFloat GetHyphenWidth() {
NS_ERROR("This shouldn't be called because we never specify hyphen breaks");
return 0;
}
virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength,
Spacing* aSpacing);
private:
const nscoord* mSpacing;
};
void
StubPropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength,
Spacing* aSpacing)
{
PRUint32 i;
for (i = 0; i < aLength; ++i) {
aSpacing[i].mBefore = 0;
// mSpacing is absolute (already includes the character width). This is OK
// because gfxTextRunCache sets TEXT_ABSOLUTE_SPACING when it creates
// the textrun.
aSpacing[i].mAfter = mSpacing ? mSpacing[aStart + i] : 0;
}
}
nsresult
nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth,
nsThebesRenderingContext *aContext)
@ -296,13 +332,12 @@ nsThebesFontMetrics::GetWidth(const char* aString, PRUint32 aLength, nscoord& aW
if ((aLength == 1) && (aString[0] == ' '))
return GetSpaceWidth(aWidth);
const nsDependentCSubstring& theString = nsDependentCSubstring(aString, aString+aLength);
//nsRefPtr<gfxTextRun> textrun = mFontGroup->MakeTextRun(theString);
nsRefPtr<gfxTextRun> textrun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(mFontGroup, theString);
StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE);
if (!textRun.get())
return NS_ERROR_FAILURE;
textrun->SetRightToLeft(mIsRTL);
aWidth = ROUND_TO_TWIPS(textrun->Measure(aContext->Thebes()));
aWidth = NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider));
return NS_OK;
}
@ -321,16 +356,14 @@ nsThebesFontMetrics::GetWidth(const PRUnichar* aString, PRUint32 aLength,
if ((aLength == 1) && (aString[0] == ' '))
return GetSpaceWidth(aWidth);
const nsDependentSubstring& theString = nsDependentSubstring(aString, aString+aLength);
//nsRefPtr<gfxTextRun> textrun = mFontGroup->MakeTextRun(theString);
nsRefPtr<gfxTextRun> textrun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(mFontGroup, theString);
StubPropertyProvider provider;
AutoTextRun textRun(this, aContext, aString, aLength, PR_FALSE);
if (!textRun.get())
return NS_ERROR_FAILURE;
textrun->SetRightToLeft(mIsRTL);
aWidth = ROUND_TO_TWIPS(textrun->Measure(aContext->Thebes()));
aWidth = NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider));
return NS_OK;
}
// Get the text dimensions for this string
@ -380,68 +413,47 @@ nsThebesFontMetrics::DrawString(const char *aString, PRUint32 aLength,
if (aLength == 0)
return NS_OK;
float app2dev = mDeviceContext->AppUnitsToDevUnits();
const nsDependentCSubstring& theString = nsDependentCSubstring(aString, aString+aLength);
//nsRefPtr<gfxTextRun> textrun = mFontGroup->MakeTextRun(theString);
nsRefPtr<gfxTextRun> textrun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(mFontGroup, theString);
textrun->SetRightToLeft(mIsRTL);
if (aSpacing) {
gfxFloat offset = aX * app2dev;
nsTArray<gfxFloat> spacing(aLength);
for (PRUint32 i = 0; i < aLength; ++i) {
gfxFloat nextOffset = offset + aSpacing[i] * app2dev;
spacing.AppendElement(NSToIntRound(nextOffset) -
NSToIntRound(offset));
offset = nextOffset;
}
textrun->SetSpacing(spacing);
StubPropertyProvider provider(aSpacing);
AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull);
if (!textRun.get())
return NS_ERROR_FAILURE;
gfxPoint pt(aX, aY);
#ifdef MOZ_X11
if (mIsRTL) {
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
}
aContext->Thebes()->DrawTextRun(textrun, gfxPoint(NSToIntRound(aX * app2dev), NSToIntRound(aY * app2dev)));
#endif
textRun->Draw(aContext->Thebes(), pt, 0, aLength,
nsnull, &provider, nsnull);
return NS_OK;
}
// aCachedOffset will be updated with a new offset.
nsresult
nsThebesFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength,
nscoord aX, nscoord aY,
PRInt32 aFontID,
const nscoord* aSpacing,
nsThebesRenderingContext *aContext)
nscoord aX, nscoord aY,
PRInt32 aFontID,
const nscoord* aSpacing,
nsThebesRenderingContext *aContext)
{
if (aLength == 0)
return NS_OK;
float app2dev = mDeviceContext->AppUnitsToDevUnits();
const nsDependentSubstring& theString = nsDependentSubstring(aString, aString+aLength);
//nsRefPtr<gfxTextRun> textrun = mFontGroup->MakeTextRun(theString);
nsRefPtr<gfxTextRun> textrun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(mFontGroup, theString);
textrun->SetRightToLeft(mIsRTL);
if (aSpacing) {
gfxFloat offset = aX * app2dev;
nsTArray<gfxFloat> spacing(aLength);
for (PRUint32 i = 0; i < aLength; ++i) {
gfxFloat nextOffset = offset + aSpacing[i] * app2dev;
spacing.AppendElement(NSToIntRound(nextOffset) -
NSToIntRound(offset));
offset = nextOffset;
}
textrun->SetSpacing(spacing);
StubPropertyProvider provider(aSpacing);
AutoTextRun textRun(this, aContext, aString, aLength, aSpacing != nsnull);
if (!textRun.get())
return NS_ERROR_FAILURE;
gfxPoint pt(aX, aY);
#ifdef MOZ_X11
if (mIsRTL) {
pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
}
aContext->Thebes()->DrawTextRun(textrun, gfxPoint(NSToIntRound(aX * app2dev), NSToIntRound(aY * app2dev)));
#endif
textRun->Draw(aContext->Thebes(), pt, 0, aLength,
nsnull, &provider, nsnull);
return NS_OK;
}
#ifdef MOZ_MATHML
// These two functions get the bounding metrics for this handle,
// updating the aBoundingMetrics in Points. This means that the

View File

@ -46,6 +46,7 @@
#include "nsIAtom.h"
#include "gfxFont.h"
#include "gfxTextRunCache.h"
class nsThebesFontMetrics : public nsIThebesFontMetrics
{
@ -143,10 +144,41 @@ public:
virtual nsresult SetRightToLeftText(PRBool aIsRTL);
virtual PRBool GetRightToLeftText();
virtual gfxFontGroup* GetThebesFontGroup() { return mFontGroup; }
protected:
const gfxFont::Metrics& GetMetrics() const;
class AutoTextRun {
public:
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const char* aString, PRInt32 aLength, PRBool aEnableSpacing) {
mTextRun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(
NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mFontGroup, aString, aLength, aMetrics->mDev2App,
aMetrics->mIsRTL, aEnableSpacing, &mOwning);
}
AutoTextRun(nsThebesFontMetrics* aMetrics, nsIRenderingContext* aRC,
const PRUnichar* aString, PRInt32 aLength, PRBool aEnableSpacing) {
mTextRun = gfxTextRunCache::GetCache()->GetOrMakeTextRun(
NS_STATIC_CAST(gfxContext*, aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)),
aMetrics->mFontGroup, aString, aLength, aMetrics->mDev2App,
aMetrics->mIsRTL, aEnableSpacing, &mOwning);
}
~AutoTextRun() {
if (mOwning) {
delete mTextRun;
}
}
gfxTextRun* operator->() { return mTextRun; }
gfxTextRun* get() { return mTextRun; }
private:
gfxTextRun* mTextRun;
PRBool mOwning;
};
friend class AutoTextRun;
nsRefPtr<gfxFontGroup> mFontGroup;
gfxFontStyle *mFontStyle;

View File

@ -22,6 +22,7 @@ EXPORTS = gfxASurface.h \
gfxPoint.h \
gfxRect.h \
gfxRegion.h \
gfxSkipChars.h \
gfxTypes.h \
gfxTextRunCache.h \
$(NULL)

View File

@ -82,10 +82,14 @@ public:
const gfxFontStyle *aStyle);
virtual ~gfxAtsuiFontGroup();
virtual gfxTextRun *MakeTextRun(const nsAString& aString);
virtual gfxTextRun *MakeTextRun(const nsACString& aCString) {
return MakeTextRun(NS_ConvertASCIItoUTF16(aCString));
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) {
NS_ERROR("NOT IMPLEMENTED");
return nsnull;
}
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams);
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams);
ATSUFontFallbacks *GetATSUFontFallbacksPtr() { return &mFallbacks; }
@ -103,7 +107,7 @@ protected:
ATSUFontFallbacks mFallbacks;
};
class THEBES_API gfxAtsuiTextRun : public gfxTextRun {
class THEBES_API gfxAtsuiTextRun {
public:
gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aFontGroup);
~gfxAtsuiTextRun();
@ -114,6 +118,9 @@ public:
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
void SetRightToLeft(PRBool aIsRTL) { mIsRTL = aIsRTL; }
PRBool IsRightToLeft() { return mIsRTL; }
private:
nsString mString;
gfxAtsuiFontGroup *mGroup;
@ -121,6 +128,8 @@ private:
ATSUTextLayout mATSULayout;
nsTArray<ATSUStyle> mStylesToDispose;
PRPackedBool mIsRTL;
};
#endif /* GFX_ATSUIFONTS_H */

View File

@ -51,7 +51,6 @@
#include "gfxFont.h"
class gfxRegion;
class gfxTextRun;
/**
* This is the main class for doing actual drawing. It is initialized using
@ -191,22 +190,6 @@ public:
void Ellipse(gfxPoint center, gfxSize dimensions);
void Polygon(const gfxPoint *points, PRUint32 numPoints);
/**
** Text
**/
/**
* Add the text outline to the current path.
*/
// specify this in a sane way.
//void AddStringToPath(gfxTextRun& text);
/**
* Draw the text run at the current point.
* XXX support drawing subsections of the text run
*/
void DrawTextRun(gfxTextRun *text, gfxPoint pt);
/**
** Transformation Matrix manipulation
**/

View File

@ -44,9 +44,12 @@
#include "nsString.h"
#include "gfxPoint.h"
#include "nsTArray.h"
#include "gfxSkipChars.h"
#include "gfxRect.h"
class gfxContext;
class gfxTextRun;
class nsIAtom;
#define FONT_STYLE_NORMAL 0
#define FONT_STYLE_ITALIC 1
@ -147,6 +150,7 @@ public:
gfxFloat strikeoutOffset;
gfxFloat underlineSize;
gfxFloat underlineOffset;
gfxFloat height;
gfxFloat internalLeading;
gfxFloat externalLeading;
@ -171,10 +175,94 @@ protected:
const gfxFontStyle *mStyle;
};
class THEBES_API gfxTextRunFactory {
THEBES_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
class THEBES_API gfxFontGroup {
THEBES_INLINE_DECL_REFCOUNTING(gfxFontGroup)
public:
// Flags >= 0x10000 are reserved for textrun clients
enum {
/**
* When set, the text string pointer used to create the text run
* is guaranteed to be available during the lifetime of the text run.
*/
TEXT_IS_PERSISTENT = 0x0001,
/**
* When set, the text is known to be all-ASCII (< 128).
*/
TEXT_IS_ASCII = 0x0002,
/**
* When set, the text is RTL.
*/
TEXT_IS_RTL = 0x0004,
/**
* When set, spacing is enabled and the textrun needs to call GetSpacing
* on the spacing provider.
*/
TEXT_ENABLE_SPACING = 0x0008,
/**
* When set, GetSpacing can return negative spacing.
*/
TEXT_ENABLE_NEGATIVE_SPACING = 0x0010,
/**
* When set, mAfter spacing for a character already includes the character
* width. Otherwise, it does not include the character width.
*/
TEXT_ABSOLUTE_SPACING = 0x0020,
/**
* When set, GetHyphenationBreaks may return true for some character
* positions, otherwise it will always return false for all characters.
*/
TEXT_ENABLE_HYPHEN_BREAKS = 0x0040,
/**
* When set, the text has no characters above 255.
*/
TEXT_IS_8BIT = 0x0080,
/**
* When set, the text may have UTF16 surrogate pairs, otherwise it
* doesn't.
*/
TEXT_HAS_SURROGATES = 0x0100
};
/**
* This record contains all the parameters needed to initialize a textrun.
*/
struct Parameters {
// A reference context suggesting where the textrun will be rendered
gfxContext* mContext;
// Pointer to arbitrary user data (which should outlive the textrun)
void* mUserData;
// The language of the text, or null if not known
nsIAtom* mLangGroup;
// A description of which characters have been stripped from the original
// DOM string to produce the characters in the textrun
gfxSkipChars* mSkipChars;
// A list of where linebreaks are currently placed in the textrun
PRUint32* mInitialBreaks;
PRUint32 mInitialBreakCount;
// The ratio to use to convert device pixels to application layout units
gfxFloat mPixelsToUnits;
// Flags --- see above
PRUint32 mFlags;
};
virtual ~gfxTextRunFactory() {}
/**
* Create a gfxTextRun from Unicode text. The length is obtained from
* aParams->mSkipChars->GetCharCount().
*/
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams) = 0;
/**
* Create a gfxTextRun from 8-bit Unicode (UCS1?) text. The length is
* obtained from aParams->mSkipChars->GetCharCount().
*/
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams) = 0;
};
class THEBES_API gfxFontGroup : public gfxTextRunFactory {
public:
gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle);
@ -196,10 +284,14 @@ public:
const gfxFontStyle *GetStyle() const { return &mStyle; }
/* unicode method */
virtual gfxTextRun *MakeTextRun(const nsAString& aString) = 0;
/* ASCII text only, not UTF-8 */
virtual gfxTextRun *MakeTextRun(const nsACString& aString) = 0;
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) = 0;
// These need to be repeated from gfxTextRunFactory because of C++'s
// hiding rules!
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams) = 0;
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams) = 0;
/* helper function for splitting font families on commas and
* calling a function for each family to fill the mFonts array
@ -232,26 +324,399 @@ protected:
static PRBool FontResolverProc(const nsAString& aName, void *aClosure);
};
// these do not copy the text
class THEBES_API gfxTextRun {
THEBES_INLINE_DECL_REFCOUNTING(gfxTextRun)
/**
* gfxTextRun is an abstraction for drawing and measuring substrings of a run
* of text.
*
* \r and \n characters must be ignored completely. Substring operations
* will not normally include these characters.
*
* gfxTextRuns are not refcounted. They should be deleted when no longer required.
*
* gfxTextRuns are mostly immutable. The only things that can change are
* inter-cluster spacing and line break placement. Spacing is always obtained
* lazily by methods that need it; cached spacing is flushed via
* FlushSpacingCache(). Line breaks are stored persistently (insofar
* as they affect the shaping of glyphs; gfxTextRun does not actually do anything
* to explicitly account for line breaks). Initially there are no line breaks.
* The textrun can record line breaks before or after any given cluster. (Line
* breaks specified inside clusters are ignored.)
*
* gfxTextRuns don't need to remember their text ... often it's enough just to
* convert text to glyphs and store glyphs plus some geometry data in packed
* form. However sometimes gfxTextRuns will want to get the original text back
* to handle some unusual situation. So gfxTextRuns have two modes: "not
* remembering text" (initial state) and "remembering text". A call to
* gfxTextRun::RememberText forces a transition from the former to latter state.
* The text is never forgotten. A gfxTextRun call that receives a TextProvider
* object may call ForceRememberText to request a transition to "remembering text".
*
* It is important that zero-length substrings are handled correctly. This will
* be on the test!
*/
class THEBES_API gfxTextRun {
public:
virtual ~gfxTextRun() {}
virtual void Draw(gfxContext *aContext, gfxPoint pt) = 0;
// returns length in pixels
virtual gfxFloat Measure(gfxContext *aContext) = 0;
enum {
// character is the start of a grapheme cluster
CLUSTER_START = 0x01,
// character is a cluster start but is part of a ligature started
// in a previous cluster.
CONTINUES_LIGATURE = 0x02,
// line break opportunity before this character
LINE_BREAK_BEFORE = 0x04
};
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
PRUint8* aFlags) = 0;
virtual PRUint8 GetCharFlags(PRUint32 aStart) = 0;
virtual void SetSpacing(const nsTArray<gfxFloat> &spacing) = 0;
virtual const nsTArray<gfxFloat> *const GetSpacing() const = 0;
virtual PRUint32 GetLength() = 0;
// defaults to FALSE
virtual void SetRightToLeft(PRBool aIsRTL) { mIsRTL = aIsRTL; }
virtual PRBool IsRightToLeft() const { return mIsRTL; }
// All PRUint32 aStart, PRUint32 aLength ranges below are restricted to
// grapheme cluster boundaries! All offsets are in terms of the string
// passed into MakeTextRun.
// All coordinates are in layout/app units
private:
PRBool mIsRTL;
/**
* This can be called to force gfxTextRun to remember the text used
* to create it and *never* call TextProvider::GetText again.
*
* The default implementation is to not remember anything. If you want
* to be able to recover text from the gfxTextRun user you need to override
* these.
*/
virtual void RememberText(const PRUnichar* aText, PRUint32 aLength) {}
virtual void RememberText(const PRUint8* aText, PRUint32 aLength) {}
/**
* Set the potential linebreaks for a substring of the textrun. These are
* the "allow break before" points. Initially, there are no potential
* linebreaks.
*
* @return true if this changed the linebreaks, false if the new line
* breaks are the same as the old
*/
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore) = 0;
/**
* This is provided so a textrun can (re)obtain the original text used to
* construct it, if necessary.
*/
class TextProvider {
public:
/**
* Recover the text originally used to build the textrun. This should
* only be requested infrequently as it may be slow. If you need to
* call it a lot you should probably be saving the text in the text run
* itself. It just forces the textrun user to call RememberText on the
* text run. If you call this and RememberText doesn't get called,
* then something has failed and you should handle it.
*/
virtual void ForceRememberText() = 0;
};
/**
* Layout provides PropertyProvider objects. These allow detection of
* potential line break points and computation of spacing. We pass the data
* this way to allow lazy data acquisition; for example BreakAndMeasureText
* will want to only ask for properties of text it's actually looking at.
*
* NOTE that requested spacing may not actually be applied, if the textrun
* is unable to apply it in some context. Exception: spacing around a
* whitespace character MUST always be applied.
*/
class PropertyProvider : public TextProvider {
public:
// Detect hyphenation break opportunities in the given range; breaks
// not at cluster boundaries will be ignored.
virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore) = 0;
// Returns the extra width that will be consumed by a hyphen. This should
// be constant for a given textrun.
virtual gfxFloat GetHyphenWidth() = 0;
/**
* We let the property provider specify spacing on either side of any
* character. We need to specify both before and after
* spacing so that substring measurement can do the right things.
*/
struct Spacing {
gfxFloat mBefore;
gfxFloat mAfter;
};
/**
* Get the spacing around the indicated characters. Spacing must be zero
* inside clusters. In other words, if character i is not
* CLUSTER_START, then character i-1 must have zero after-spacing and
* character i must have zero before-spacing.
*/
virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength,
Spacing* aSpacing) = 0;
};
/**
* Draws a substring. Uses only GetSpacing from aBreakProvider.
* The provided point is the baseline origin on the left of the string
* for LTR, on the right of the string for RTL.
* @param aDirtyRect if non-null, drawing outside of the rectangle can be
* (but does not need to be) dropped. Note that if this is null, we cannot
* draw partial ligatures and we will assert if partial ligatures
* are detected.
* @param aAdvanceWidth if non-null, the advance width of the substring
* is returned here.
*
* Drawing should respect advance widths in the sense that for LTR runs,
* Draw(ctx, pt, offset1, length1, dirty, &provider, &advance) followed by
* Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1 + length1, length2,
* dirty, &provider, nsnull) should have the same effect as
* Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nsnull).
* For RTL runs the rule is:
* Draw(ctx, pt, offset1 + length1, length2, dirty, &provider, &advance) followed by
* Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1, length1,
* dirty, &provider, nsnull) should have the same effect as
* Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nsnull).
*
* Glyphs should be drawn in logical content order, which can be significant
* if they overlap (perhaps due to negative spacing).
*/
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aProvider,
gfxFloat* aAdvanceWidth) = 0;
/**
* Renders a substring to a path. Uses only GetSpacing from aBreakProvider.
* The provided point is the baseline origin on the left of the string
* for LTR, on the right of the string for RTL.
* @param aAdvanceWidth if non-null, the advance width of the substring
* is returned here.
*
* Drawing should respect advance widths in the way that Draw above does.
*
* Glyphs should be drawn in logical content order.
*
* UNLIKE Draw above, this cannot be used to render substrings that start or
* end inside a ligature.
*/
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth) = 0;
/**
* Special strings are strings that we might need to draw/measure but aren't
* actually substrings of the given text. They never have extra spacing and
* aren't involved in breaking.
*/
enum SpecialString {
STRING_ELLIPSIS,
STRING_HYPHEN,
STRING_SPACE,
STRING_MAX = STRING_SPACE
};
/**
* Draw special string.
* The provided point is the baseline origin on the left of the string
* for LTR, on the right of the string for RTL.
*/
virtual void DrawSpecialString(gfxContext *aContext, gfxPoint aPt,
SpecialString aString) = 0;
// Metrics needed by reflow
struct Metrics {
Metrics() {
mAdvanceWidth = mAscent = mDescent = 0.0;
mBoundingBox = gfxRect(0,0,0,0);
mClusterCount = 0;
}
void CombineWith(const Metrics& aOtherOnRight) {
mAscent = PR_MAX(mAscent, aOtherOnRight.mAscent);
mDescent = PR_MAX(mDescent, aOtherOnRight.mDescent);
mBoundingBox =
mBoundingBox.Union(aOtherOnRight.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
mAdvanceWidth += aOtherOnRight.mAdvanceWidth;
mClusterCount += aOtherOnRight.mClusterCount;
}
// can be negative (partly due to negative spacing).
// Advance widths should be additive: the advance width of the
// (offset1, length1) plus the advance width of (offset1 + length1,
// length2) should be the advance width of (offset1, length1 + length2)
gfxFloat mAdvanceWidth;
// For zero-width substrings, these must be zero!
gfxFloat mAscent; // always non-negative
gfxFloat mDescent; // always non-negative
// Bounding box that is guaranteed to include everything drawn.
// If aTightBoundingBox was set to true when these metrics were
// generated, this will tightly wrap the glyphs, otherwise it is
// "loose" and may be larger than the true bounding box.
// Coordinates are relative to the baseline left origin, so typically
// mBoundingBox.y == -mAscent
gfxRect mBoundingBox;
// Count of the number of grapheme clusters. Layout needs
// this to compute tab offsets. For SpecialStrings, this is always 1.
PRInt32 mClusterCount;
};
/**
* Computes the ReflowMetrics for a substring.
* Uses GetSpacing from aBreakProvider.
* @param aTightBoundingBox if true, we make the bounding box tight
*/
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
PRBool aTightBoundingBox,
PropertyProvider* aProvider) = 0;
/**
* Computes the ReflowMetrics for a special string.
* Uses GetSpacing from aBreakProvider.
* @param aTightBoundingBox if true, we make the bounding box tight
*/
virtual Metrics MeasureTextSpecialString(SpecialString aString,
PRBool aTightBoundingBox) = 0;
/**
* Computes just the advance width for a substring.
* Uses GetSpacing from aBreakProvider.
*/
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aProvider) = 0;
/**
* Computes the advance width for a special string.
*/
virtual gfxFloat GetAdvanceWidthSpecialString(SpecialString aString) = 0;
/**
* Get the font metrics that we should use for drawing text decorations.
* Overriden here so that transforming gfxTextRuns such as smallcaps
* can do something special if they want to.
*/
virtual gfxFont::Metrics GetDecorationMetrics() = 0;
/**
* Clear all stored line breaks for the given range (both before and after),
* and then set the line-break state before aStart to aBreakBefore and
* after the last cluster to aBreakAfter.
*
* We require that before and after line breaks be consistent. For clusters
* i and i+1, we require that if there is a break after cluster i, a break
* will be specified before cluster i+1. This may be temporarily violated
* (e.g. after reflowing line L and before reflowing line L+1); to handle
* these temporary violations, we say that there is a break betwen i and i+1
* if a break is specified after i OR a break is specified before i+1.
*
* This can change textrun geometry! The existence of a linebreak can affect
* the advance width of the cluster before the break (when kerning) or the
* geometry of one cluster before the break or any number of clusters
* after the break. (The one-cluster-before-the-break limit is somewhat
* arbitrary; if some scripts require breaking it, then we need to
* alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase
* it could affect the layout of frames before it...)
*
* @param aAdvanceWidthDelta if non-null, returns the change in advance
* width of the given range.
*/
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
TextProvider* aProvider,
gfxFloat* aAdvanceWidthDelta) = 0;
/**
* Finds the longest substring that will fit into the given width.
* Uses GetHyphenationBreaks and GetSpacing from aBreakProvider.
* Guarantees the following:
* -- 0 <= result <= aMaxLength
* -- result is the maximal value of N such that either
* N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
* OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
* OR N == aMaxLength && GetAdvanceWidth(aStart, N) <= aWidth
* where GetAdvanceWidth assumes the effect of
* SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
* -- if no such N exists, then result is the smallest N such that
* N < aMaxLength && line break at N
* OR N < aMaxLength && hyphen break at N
* OR N == aMaxLength
*
* The call has the effect of
* SetLineBreaks(aStart, result, aLineBreakBefore, result < aMaxLength, aProvider)
* and the returned metrics and the invariants above reflect this.
*
* @param aMaxLength this can be PR_UINT32_MAX, in which case the length used
* is up to the end of the string
* @param aLineBreakBefore set to true if and only if there is an actual
* line break at the start of this string.
* @param aSuppressInitialBreak if true, then we assume there is no possible
* linebreak before aStart. If false, then we will check the internal
* line break opportunity state before deciding whether to return 0 as the
* character to break before.
* @param aMetrics if non-null, we fill this in for the returned substring.
* If a hyphenation break was used, the hyphen is NOT included in the returned metrics.
* @param aTightBoundingBox if true, we make the bounding box in aMetrics tight
* @param aUsedHyphenation if non-null, records if we selected a hyphenation break
* @param aLastBreak if non-null and result is aMaxLength, we set this to
* the maximal N such that
* N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
* OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
* or PR_UINT32_MAX if no such N exists, where GetAdvanceWidth assumes
* the effect of
* SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
*
* Note that negative advance widths are possible especially if negative
* spacing is provided.
*/
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool aLineBreakBefore, gfxFloat aWidth,
PropertyProvider* aProvider,
PRBool aSuppressInitialBreak,
Metrics* aMetrics, PRBool aTightBoundingBox,
PRBool* aUsedHyphenation,
PRUint32* aLastBreak) = 0;
/**
* Update the reference context.
* XXX this is a hack. New text frame does not call this. Use only
* temporarily for old text frame.
*/
virtual void SetContext(gfxContext* aContext) {}
/**
* Flush cached spacing data for the characters at and after aStart.
*/
virtual void FlushSpacingCache(PRUint32 aStart) = 0;
// Utility getters
PRBool IsRightToLeft() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0; }
gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; }
void* GetUserData() const { return mUserData; }
PRUint32 GetFlags() const { return mFlags; }
const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
gfxFloat GetPixelsToAppUnits() { return mPixelsToAppUnits; }
protected:
gfxTextRun(gfxTextRunFactory::Parameters* aParams, PRBool aIs8Bit)
: mUserData(aParams->mUserData), mPixelsToAppUnits(aParams->mPixelsToUnits),
mFlags(aParams->mFlags)
{
mSkipChars.TakeFrom(aParams->mSkipChars);
if (aIs8Bit) {
mFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
}
}
void* mUserData;
gfxSkipChars mSkipChars;
gfxFloat mPixelsToAppUnits;
PRUint32 mFlags;
};
#endif

View File

@ -45,8 +45,12 @@
#include <pango/pango.h>
#include <X11/Xft/Xft.h>
//#define USE_XFT_FOR_ASCII
#include "nsDataHashtable.h"
class FontSelector;
class gfxPangoFont : public gfxFont {
public:
gfxPangoFont (const nsAString& aName,
@ -86,8 +90,12 @@ public:
const gfxFontStyle *aStyle);
virtual ~gfxPangoFontGroup ();
virtual gfxTextRun *MakeTextRun(const nsAString& aString);
virtual gfxTextRun *MakeTextRun(const nsACString& aCString);
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams);
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams);
gfxPangoFont *GetFontAt(PRInt32 i) {
return NS_STATIC_CAST(gfxPangoFont*,
@ -104,72 +112,344 @@ public:
void PutCachedFont(const nsAString& aName, gfxPangoFont *aFont) {
mFontCache.Put(aName, aFont);
}
struct SpecialStringData {
PangoGlyphString* mGlyphs;
gfxFloat mAdvance;
SpecialStringData() { mGlyphs = nsnull; }
~SpecialStringData() { if (mGlyphs) pango_glyph_string_free(mGlyphs); }
};
SpecialStringData mSpecialStrings[gfxTextRun::STRING_MAX + 1];
protected:
static PRBool FontCallback (const nsAString& fontName,
const nsACString& genericName,
void *closure);
private:
nsDataHashtable<nsStringHashKey, nsRefPtr<gfxPangoFont> > mFontCache;
nsTArray<gfxFontStyle> mAdditionalStyles;
};
#ifdef USE_XFT_FOR_ASCII
class THEBES_API gfxXftTextRun : public gfxTextRun {
public:
gfxXftTextRun(const nsAString& aString, gfxPangoFontGroup *aFontGroup);
gfxXftTextRun(const nsACString& aString, gfxPangoFontGroup *aFontGroup);
gfxXftTextRun(gfxPangoFontGroup *aGroup,
const char* aString, PRInt32 aLength, PRUint32 aFlags);
~gfxXftTextRun();
virtual void Draw(gfxContext *aContext, gfxPoint pt);
virtual gfxFloat Measure(gfxContext *aContext);
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
virtual nsresult Init(PRBool aIsRTL, nsIAtom* aLangGroup,
gfxFloat aPixelsToUnits,
gfxSkipChars* aSkipChars, void* aUserData);
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
PRUint8* aFlags);
virtual PRUint8 GetCharFlag(PRUint32 aOffset);
virtual void Draw(gfxContext* aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth);
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
SpecialString aString);
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
PRBool aTightBoundingBox,
PropertyProvider* aBreakProvider);
virtual Metrics MeasureText(SpecialString aString,
PRBool aTightBoundingBox);
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider);
virtual gfxFloat GetAdvanceWidth(SpecialString aString);
virtual gfxFont::Metrics& GetDecorationMetrics();
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
gfxFloat aWidth,
PropertyProvider* aBreakProvider,
PRBool aSuppressInitialBreak,
Metrics* aMetrics, PRBool aTightBoundingBox,
PRBool* aUsedHyphenation,
PRUint32* aLastBreak);
virtual void FlushSpacingCache(PRUint32 aStart, PRUint32 aLength);
private:
nsString mWString;
nsCString mCString;
PRBool mIsWide;
gfxPangoFontGroup *mGroup;
int mWidth, mHeight;
nsTArray<gfxFloat> mSpacing;
nsTArray<PRInt32> mUTF8Spacing;
};
#endif
struct TextSegment;
class THEBES_API gfxPangoTextRun : public gfxTextRun {
public:
gfxPangoTextRun(const nsAString& aString, gfxPangoFontGroup *aFontGroup);
gfxPangoTextRun(gfxPangoFontGroup *aGroup,
const PRUint8* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams);
gfxPangoTextRun(gfxPangoFontGroup *aGroup,
const PRUnichar* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams);
~gfxPangoTextRun();
virtual void Draw(gfxContext *aContext, gfxPoint pt);
virtual gfxFloat Measure(gfxContext *aContext);
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
PRUint8* aFlags);
virtual PRUint8 GetCharFlags(PRUint32 aOffset);
virtual PRUint32 GetLength();
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore);
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth);
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth);
virtual void DrawSpecialString(gfxContext* aContext, gfxPoint aPt,
SpecialString aString);
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
PRBool aTightBoundingBox,
PropertyProvider* aBreakProvider);
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
TextProvider* aProvider,
gfxFloat* aAdvanceWidthDelta);
virtual Metrics MeasureTextSpecialString(SpecialString aString,
PRBool aTightBoundingBox);
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider);
virtual gfxFloat GetAdvanceWidthSpecialString(SpecialString aString);
virtual gfxFont::Metrics GetDecorationMetrics();
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool aLineBreakBefore, gfxFloat aWidth,
PropertyProvider* aBreakProvider,
PRBool aSuppressInitialBreak,
Metrics* aMetrics, PRBool aTightBoundingBox,
PRBool* aUsedHyphenation,
PRUint32* aLastBreak);
virtual void FlushSpacingCache(PRUint32 aStart);
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
/**
* This class records the information associated with a character in the
* input string. It's optimized for the case where there is one glyph
* representing that character alone.
*/
class CompressedGlyph {
public:
CompressedGlyph() { mValue = 0; }
enum {
// Indicates that a cluster starts at this character and can be
// rendered using a single glyph with a reasonable advance offset
// and no special glyph offset. A "reasonable" advance offset is
// one that is a) a multiple of a pixel and b) fits in the available
// bits (currently 14). We should revisit this, especially a),
// if we want to support subpixel-aligned text.
FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
// Indicates that a linebreak is allowed before this character
FLAG_CAN_BREAK_BEFORE = 0x40000000U,
ADVANCE_MASK = 0x3FFF0000U,
ADVANCE_SHIFT = 16,
GLYPH_MASK = 0x0000FFFFU,
// Non-simple glyphs have the following tags
TAG_MASK = 0x000000FFU,
// Indicates that a cluster starts at this character and is rendered
// using one or more glyphs which cannot be represented here.
// Look up the DetailedGlyph table instead.
TAG_COMPLEX_CLUSTER = 0x00U,
// Indicates that a cluster starts at this character but is rendered
// as part of a ligature starting in a previous cluster.
// NOTE: we divide up the ligature's width by the number of clusters
// to get the width assigned to each cluster.
TAG_LIGATURE_CONTINUATION = 0x01U,
// Values where the upper 28 bits equal 0x80 are reserved for
// non-cluster-start characters (see IsClusterStart below)
// Indicates that a cluster does not start at this character, this is
// a low UTF16 surrogate
TAG_LOW_SURROGATE = 0x80U,
// Indicates that a cluster does not start at this character, this is
// part of a cluster starting with an earlier character (but not
// a low surrogate).
TAG_CLUSTER_CONTINUATION = 0x81U
};
// Returns true if the glyph ID aGlyph fits into the compressed representation
static PRBool IsSimpleGlyphID(PRUint32 aGlyph) {
return (aGlyph & GLYPH_MASK) == aGlyph;
}
// Returns true if the advance aAdvance fits into the compressed representation
static PRBool IsSimpleAdvance(PRUint32 aAdvance) {
return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
}
PRBool IsSimpleGlyph() { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
PRBool IsComplex(PRUint32 aTag) { return (mValue & (FLAG_IS_SIMPLE_GLYPH|TAG_MASK)) == aTag; }
PRBool IsComplexCluster() { return IsComplex(TAG_COMPLEX_CLUSTER); }
PRBool IsLigatureContinuation() { return IsComplex(TAG_LIGATURE_CONTINUATION); }
PRBool IsLowSurrogate() { return IsComplex(TAG_LOW_SURROGATE); }
PRBool IsClusterStart() { return (mValue & (FLAG_IS_SIMPLE_GLYPH|0x80U)) != 0x80U; }
PRUint32 GetSimpleAdvance() { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
PRUint32 GetSimpleGlyph() { return mValue & GLYPH_MASK; }
PRUint32 GetComplexTag() { return mValue & TAG_MASK; }
PRBool CanBreakBefore() { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; }
// Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
PRUint32 SetCanBreakBefore(PRBool aCanBreakBefore) {
PRUint32 breakMask = aCanBreakBefore*FLAG_CAN_BREAK_BEFORE;
PRUint32 toggle = breakMask ^ (mValue & FLAG_CAN_BREAK_BEFORE);
mValue ^= toggle;
return toggle;
}
void SetSimpleGlyph(PRUint32 aAdvance, PRUint32 aGlyph) {
NS_ASSERTION(IsSimpleAdvance(aAdvance), "Advance overflow");
NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_IS_SIMPLE_GLYPH |
(aAdvance << ADVANCE_SHIFT) | aGlyph;
}
void SetComplex(PRUint32 aTag) { mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | aTag; }
void SetComplexCluster() { SetComplex(TAG_COMPLEX_CLUSTER); }
void SetLowSurrogate() { SetComplex(TAG_LOW_SURROGATE); }
void SetLigatureContinuation() { SetComplex(TAG_LIGATURE_CONTINUATION); }
void SetClusterContinuation() { SetComplex(TAG_CLUSTER_CONTINUATION); }
private:
PRUint32 mValue;
};
struct DetailedGlyph {
PRUint32 mIsLastGlyph:1;
PRUint32 mGlyphID:31;
float mAdvance, mXOffset, mYOffset;
};
// The text is divided into GlyphRuns at Pango item and text segment boundaries
struct GlyphRun {
PangoFont* mPangoFont; // can't be null
cairo_scaled_font_t* mCairoFont; // could be null
PRUint32 mCharacterOffset; // into original UTF16 string
~GlyphRun() {
if (mCairoFont) {
cairo_scaled_font_destroy(mCairoFont);
}
}
};
class GlyphRunIterator {
public:
GlyphRunIterator(gfxPangoTextRun* aTextRun, PRUint32 aStart, PRUint32 aLength)
: mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart);
}
PRBool NextRun();
GlyphRun* GetGlyphRun() { return mGlyphRun; }
PRUint32 GetStringStart() { return mStringStart; }
PRUint32 GetStringEnd() { return mStringEnd; }
private:
gfxPangoTextRun* mTextRun;
GlyphRun* mGlyphRun;
PRUint32 mStringStart;
PRUint32 mStringEnd;
PRUint32 mNextIndex;
PRUint32 mStartOffset;
PRUint32 mEndOffset;
};
friend class GlyphRunIterator;
friend class FontSelector;
// **** Initialization helpers ****
private:
nsString mString;
nsCString mUTF8String;
// If aUTF16Text is null, then the string contains no characters >= 0x100
void Init(gfxTextRunFactory::Parameters* aParams, const gchar* aUTF8Text,
PRUint32 aUTF8Length, const PRUnichar* aUTF16Text,
PRUint32 aUTF16Length);
void SetupClusterBoundaries(const gchar* aUTF8, PRUint32 aUTF8Length,
PRUint32 aUTF16Offset, PangoAnalysis* aAnalysis);
nsresult AddGlyphRun(PangoFont* aFont, PRUint32 aUTF16Offset);
// Returns NS_ERROR_FAILURE if there's a missing glyph
nsresult SetGlyphs(const gchar* aUTF8, PRUint32 aUTF8Length,
PRUint32* aUTF16Offset, PangoGlyphString* aGlyphs,
PangoGlyphUnit aOverrideSpaceWidth,
PRBool aAbortOnMissingGlyph);
// If aUTF16Text is null, then the string contains no characters >= 0x100.
// Returns NS_ERROR_FAILURE if we require the itemizing path
nsresult CreateGlyphRunsFast(const gchar* aUTF8, PRUint32 aUTF8Length,
const PRUnichar* aUTF16Text, PRUint32 aUTF16Length);
void CreateGlyphRunsItemizing(const gchar* aUTF8, PRUint32 aUTF8Length);
gfxPangoFontGroup *mGroup;
// **** general helpers ****
PangoLayout *mPangoLayout;
int mWidth;
void SetupPangoContextDirection();
static void SetupCairoFont(cairo_t* aCR, GlyphRun* aGlyphRun);
// Returns the index of the GlyphRun containing the given offset.
// Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
// Computes the x-advance for a given cluster starting at aClusterOffset. Does
// not include any spacing. Result is in device pixels.
gfxFloat ComputeClusterAdvance(PRUint32 aClusterOffset);
int MeasureOrDraw(gfxContext *aContext, PRBool aDraw, gfxPoint aPt);
int MeasureOrDrawItemizing(gfxContext *aContext, PRBool aDraw,
gfxPoint aPt, PRBool aIsRTL,
gfxPangoFont *aFont);
int DrawFromCache(gfxContext *aContext, gfxPoint aPt);
// **** ligature helpers ****
// (Pango does the actual ligaturization, but we need to do a bunch of stuff
// to handle requests that begin or end inside a ligature)
nsTArray<gfxFloat> mSpacing;
nsTArray<PRInt32> mUTF8Spacing;
nsTArray<TextSegment> mSegments;
struct LigatureData {
PRUint32 mStartOffset;
PRUint32 mEndOffset;
PRUint32 mClusterCount;
PRUint32 mPartClusterIndex;
gfxFloat mLigatureWidth; // appunits
gfxFloat mBeforeSpacing; // appunits
gfxFloat mAfterSpacing; // appunits
};
// if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
LigatureData ComputeLigatureData(PRUint32 aPartOffset, PropertyProvider* aProvider);
void GetAdjustedSpacing(PRUint32 aStart, PRUint32 aEnd,
PropertyProvider* aProvider, PropertyProvider::Spacing* aSpacing);
PRBool GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
PropertyProvider* aProvider,
nsTArray<PropertyProvider::Spacing>* aSpacing);
void DrawPartialLigature(gfxContext* aCtx, PRUint32 aOffset,
const gfxRect* aDirtyRect, gfxPoint* aPt,
PropertyProvider* aProvider);
// result in appunits
void ShrinkToLigatureBoundaries(PRUint32* aStart, PRUint32* aEnd);
gfxFloat GetPartialLigatureWidth(PRUint32 aStart, PRUint32 aEnd, PropertyProvider* aProvider);
void AccumulatePartialLigatureMetrics(PangoFont* aPangoFont,
PRUint32 aOffset, PropertyProvider* aProvider,
Metrics* aMetrics);
// **** measurement helper ****
void AccumulatePangoMetricsForRun(PangoFont* aPangoFont, PRUint32 aStart,
PRUint32 aEnd, PropertyProvider* aProvider,
Metrics* aMetrics);
// **** drawing helpers ****
typedef void (* CairoGlyphProcessorCallback)
(void* aClosure, cairo_glyph_t* aGlyphs, int aNumGlyphs);
void ProcessCairoGlyphsWithSpacing(CairoGlyphProcessorCallback aCB, void* aClosure,
gfxPoint* aPt, PRUint32 aStart, PRUint32 aEnd,
PropertyProvider::Spacing* aSpacing);
void ProcessCairoGlyphs(CairoGlyphProcessorCallback aCB, void* aClosure,
gfxPoint* aPt, PRUint32 aStart, PRUint32 aEnd,
PropertyProvider* aProvider);
static void CairoShowGlyphs(void* aClosure, cairo_glyph_t* aGlyphs, int aNumGlyphs);
static void CairoGlyphsToPath(void* aClosure, cairo_glyph_t* aGlyphs, int aNumGlyphs);
nsRefPtr<gfxPangoFontGroup> mFontGroup;
// All our glyph data is in logical order, not visual
nsAutoArrayPtr<CompressedGlyph> mCharacterGlyphs;
nsAutoArrayPtr<nsAutoArrayPtr<DetailedGlyph> > mDetailedGlyphs; // only non-null if needed
nsAutoTArray<GlyphRun,1> mGlyphRuns;
PRUint32 mCharacterCount;
};
#endif /* GFX_PANGOFONTS_H */

View File

@ -59,12 +59,25 @@ struct THEBES_API gfxRect {
return (pos != s.pos) || (size != s.size);
}
const gfxRect& MoveBy(const gfxPoint& aPt) {
pos = pos + aPt;
return *this;
}
gfxRect operator+(const gfxPoint& aPt) const {
return gfxRect(pos + aPt, size);
}
const gfxPoint& TopLeft() const { return pos; }
gfxFloat Width() const { return size.width; }
gfxFloat Height() const { return size.height; }
gfxFloat X() const { return pos.x; }
gfxFloat Y() const { return pos.y; }
gfxFloat XMost() const { return pos.x + size.width; }
gfxFloat YMost() const { return pos.y + size.height; }
PRBool IsEmpty() const { return size.width <= 0 || size.height <= 0; }
gfxRect Intersect(const gfxRect& aRect) const;
gfxRect Union(const gfxRect& aRect) const;
// XXX figure out what methods (intersect, union, etc) we use and add them.
};

View File

@ -0,0 +1,333 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_SKIP_CHARS_H
#define GFX_SKIP_CHARS_H
#include "prtypes.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "gfxTypes.h"
/*
* gfxSkipChars is a data structure representing a list of characters that
* have been skipped. The initial string is called the "original string"
* and after skipping some characters, the result is called the "skipped string".
* gfxSkipChars provides efficient ways to translate between offsets in the
* original string and the skipped string. It is used by textrun code to keep
* track of offsets before and after text transformations such as whitespace
* compression and control code deletion.
*/
/**
* gfxSkipCharsBuilder is a helper class that accumulates a list of (skip, keep)
* commands and can eventually be used to construct a real gfxSkipChars.
* gfxSkipCharsBuilder objects are quite large so don't keep these around.
* On the positive side, the Skip/KeepChar(s) methods are very efficient,
* especially when you have runs of all-kept or all-skipped characters.
*
* mBuffer is an array of bytes; even numbered bytes represent characters kept,
* odd numbered bytes represent characters skipped. After those characters
* are accounted for, we have mRunCharCount characters which are kept or
* skipped depending on the value of mRunSkipped.
*
* mCharCount is the sum of counts of all skipped and kept characters, i.e.,
* the length of the original string.
*/
class THEBES_API gfxSkipCharsBuilder {
public:
gfxSkipCharsBuilder() :
mCharCount(0), mRunCharCount(0), mRunSkipped(PR_FALSE), mInErrorState(PR_FALSE)
{}
void SkipChars(PRUint32 aChars) {
DoChars(aChars, PR_TRUE);
}
void KeepChars(PRUint32 aChars) {
DoChars(aChars, PR_FALSE);
}
void SkipChar() {
SkipChars(1);
}
void KeepChar() {
KeepChars(1);
}
void DoChars(PRUint32 aChars, PRBool aSkipped) {
if (aSkipped != mRunSkipped && aChars > 0) {
FlushRun();
}
NS_ASSERTION(mRunCharCount + aChars > mRunCharCount,
"Character count overflow");
mRunCharCount += aChars;
}
PRBool IsOK() { return !mInErrorState; }
PRUint32 GetCharCount() { return mCharCount + mRunCharCount; }
PRBool GetAllCharsKept() { return mBuffer.Length() == 0; }
friend class gfxSkipChars;
private:
typedef nsAutoTArray<PRUint8,256> Buffer;
/**
* Moves mRunCharCount/mRunSkipped to the buffer (updating mCharCount),
* sets mRunCharCount to zero and toggles mRunSkipped.
*/
void FlushRun();
Buffer mBuffer;
PRUint32 mCharCount;
PRUint32 mRunCharCount;
PRPackedBool mRunSkipped; // == mBuffer.Length()&1
PRPackedBool mInErrorState;
};
/**
* The gfxSkipChars list is represented as a list of bytes of the form
* [chars to keep, chars to skip, chars to keep, chars to skip, ...]
* In the special case where all chars are to be kept, the list is length
* zero.
*
* A freshly-created gfxSkipChars means "all chars kept".
*/
class THEBES_API gfxSkipChars {
public:
gfxSkipChars() : mListLength(0), mCharCount(0) {}
void TakeFrom(gfxSkipChars* aSkipChars) {
mList = aSkipChars->mList.forget();
mListLength = aSkipChars->mListLength;
mCharCount = aSkipChars->mCharCount;
aSkipChars->mCharCount = 0;
aSkipChars->mListLength = 0;
BuildShortcuts();
}
void TakeFrom(gfxSkipCharsBuilder* aSkipCharsBuilder) {
if (!aSkipCharsBuilder->mBuffer.Length()) {
NS_ASSERTION(!aSkipCharsBuilder->mRunSkipped, "out of sync");
// all characters kept
mCharCount = aSkipCharsBuilder->mRunCharCount;
mList = nsnull;
mListLength = 0;
} else {
aSkipCharsBuilder->FlushRun();
mCharCount = aSkipCharsBuilder->mCharCount;
mList = new PRUint8[aSkipCharsBuilder->mBuffer.Length()];
if (!mList) {
mListLength = 0;
} else {
mListLength = aSkipCharsBuilder->mBuffer.Length();
memcpy(mList, aSkipCharsBuilder->mBuffer.Elements(), mListLength);
}
}
aSkipCharsBuilder->mBuffer.Clear();
aSkipCharsBuilder->mCharCount = 0;
aSkipCharsBuilder->mRunCharCount = 0;
aSkipCharsBuilder->mRunSkipped = PR_FALSE;
BuildShortcuts();
}
void SetAllKeep(PRUint32 aLength) {
mCharCount = aLength;
mList = nsnull;
mListLength = 0;
}
PRInt32 GetOriginalCharCount() const { return mCharCount; }
friend class gfxSkipCharsIterator;
private:
struct Shortcut {
PRUint32 mListPrefixLength;
PRUint32 mListPrefixCharCount;
PRUint32 mListPrefixKeepCharCount;
Shortcut() {}
Shortcut(PRUint32 aListPrefixLength, PRUint32 aListPrefixCharCount,
PRUint32 aListPrefixKeepCharCount) :
mListPrefixLength(aListPrefixLength),
mListPrefixCharCount(aListPrefixCharCount),
mListPrefixKeepCharCount(aListPrefixKeepCharCount) {}
};
void BuildShortcuts();
nsAutoArrayPtr<PRUint8> mList;
nsAutoArrayPtr<Shortcut> mShortcuts;
PRUint32 mListLength;
PRUint32 mCharCount;
};
/**
* A gfxSkipCharsIterator represents a position in the original string. It lets you
* map efficiently to and from positions in the string after skipped characters
* have been removed. You can also specify an offset that is added to all
* incoming original string offsets and subtracted from all outgoing original
* string offsets --- useful when the gfxSkipChars corresponds to something
* offset from the original DOM coordinates, which it often does for gfxTextRuns.
*
* The current positions (in both the original and skipped strings) are
* always constrained to be >= 0 and <= the string length. When the position
* is equal to the string length, it is at the end of the string. The current
* positions do not include any aOriginalStringToSkipCharsOffset.
*/
class THEBES_API gfxSkipCharsIterator {
public:
/**
* @param aOriginalStringToSkipCharsOffset add this to all incoming and
* outgoing original string offsets
*/
gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
PRInt32 aOriginalStringToSkipCharsOffset,
PRInt32 aOriginalStringOffset)
: mSkipChars(&aSkipChars),
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
SetOriginalOffset(aOriginalStringOffset);
}
gfxSkipCharsIterator(const gfxSkipChars& aSkipChars,
PRInt32 aOriginalStringToSkipCharsOffset = 0)
: mSkipChars(&aSkipChars),
mOriginalStringOffset(0), mSkippedStringOffset(0),
mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset),
mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) {
}
gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator)
: mSkipChars(aIterator.mSkipChars),
mOriginalStringOffset(aIterator.mOriginalStringOffset),
mSkippedStringOffset(aIterator.mSkippedStringOffset),
mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset),
mListPrefixLength(aIterator.mListPrefixLength),
mListPrefixCharCount(aIterator.mListPrefixCharCount),
mListPrefixKeepCharCount(aIterator.mListPrefixKeepCharCount)
{}
/**
* The empty constructor creates an object that is useless until it is assigned.
*/
gfxSkipCharsIterator() : mSkipChars(nsnull) {}
/**
* Set the iterator to aOriginalStringOffset in the original string.
* This can efficiently move forward or backward from the current position.
* aOriginalStringOffset is clamped to [0,originalStringLength].
*/
void SetOriginalOffset(PRInt32 aOriginalStringOffset) {
SetOffsets(aOriginalStringOffset + mOriginalStringToSkipCharsOffset, PR_TRUE);
}
/**
* Set the iterator to aSkippedStringOffset in the skipped string.
* This can efficiently move forward or backward from the current position.
* aSkippedStringOffset is clamped to [0,skippedStringLength].
*/
void SetSkippedOffset(PRUint32 aSkippedStringOffset) {
SetOffsets(aSkippedStringOffset, PR_FALSE);
}
PRUint32 ConvertOriginalToSkipped(PRInt32 aOriginalStringOffset) {
SetOriginalOffset(aOriginalStringOffset);
return GetSkippedOffset();
}
PRUint32 ConvertSkippedToOriginal(PRInt32 aSkippedStringOffset) {
SetSkippedOffset(aSkippedStringOffset);
return GetOriginalOffset();
}
/**
* Test if the character at the current position in the original string
* is skipped or not. If aRunLength is non-null, then *aRunLength is set
* to a number of characters all of which are either skipped or not, starting
* at this character. When the current position is at the end of the original
* string, we return PR_TRUE and *aRunLength is set to zero.
*/
PRBool IsOriginalCharSkipped(PRInt32* aRunLength = nsnull) const;
void AdvanceOriginal(PRInt32 aDelta) {
SetOffsets(mOriginalStringOffset + aDelta, PR_TRUE);
}
void AdvanceSkipped(PRInt32 aDelta) {
SetOffsets(mSkippedStringOffset + aDelta, PR_FALSE);
}
/**
* @return the offset within the original string
*/
PRInt32 GetOriginalOffset() const {
return mOriginalStringOffset - mOriginalStringToSkipCharsOffset;
}
/**
* @return the offset within the skipped string corresponding to the
* current position in the original string. If the current position
* in the original string is a character that is skipped, then we return
* the position corresponding to the first non-skipped character in the
* original string after the current position, or the length of the skipped
* string if there is no such character.
*/
PRUint32 GetSkippedOffset() const { return mSkippedStringOffset; }
private:
void SetOffsets(PRUint32 aOffset, PRBool aInOriginalString);
const gfxSkipChars* mSkipChars;
PRInt32 mOriginalStringOffset;
PRUint32 mSkippedStringOffset;
PRUint32 mOriginalStringToSkipCharsOffset;
/*
* This is used to speed up cursor-style traversal. The invariant is that
* the first mListPrefixLength bytes of mSkipChars.mList sum to
* mListPrefixCharCount, and the even-indexed bytes in that prefix sum to
* mListPrefixKeepCharCount.
* Also, 0 <= mListPrefixLength < mSkipChars.mListLength, or else
* mSkipChars.mListLength is zero.
* Also, mListPrefixCharCount <= mOriginalStringOffset (and therefore
* mListPrefixKeepCharCount < mSkippedStringOffset).
*/
PRUint32 mListPrefixLength;
PRUint32 mListPrefixCharCount;
PRUint32 mListPrefixKeepCharCount;
};
#endif /*GFX_SKIP_CHARS_H*/

View File

@ -52,15 +52,17 @@ public:
*/
static gfxTextRunCache* GetCache();
/* Will return a pointer to a gfxTextRun, either from the cache
* or a newly created (and cached) run.
*
* The caller does not have to addref the result, as long as
* it is not used past another call to GetOrMakeTextRun, at which point
* it may be evicted.
/* Will return a pointer to a gfxTextRun, which may or may not be from
* the cache. If aCallerOwns is set to true, the caller owns the textrun
* and must delete it. Otherwise the returned textrun is only valid until
* the next GetOrMakeTextRun call and the caller must not delete it.
*/
gfxTextRun *GetOrMakeTextRun (gfxFontGroup *aFontGroup, const nsAString& aString);
gfxTextRun *GetOrMakeTextRun (gfxFontGroup *aFontGroup, const nsACString& aString);
gfxTextRun *GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
const char *aString, PRUint32 aLength, gfxFloat aDevToApp,
PRBool aIsRTL, PRBool aEnableSpacing, PRBool *aCallerOwns);
gfxTextRun *GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
const PRUnichar *aString, PRUint32 aLength, gfxFloat aDevToApp,
PRBool aIsRTL, PRBool aEnableSpacing, PRBool *aCallerOwns);
protected:
gfxTextRunCache();
@ -139,8 +141,10 @@ protected:
TextRunEntry(gfxTextRun *tr) : textRun(tr), lastUse(PR_Now()) { }
void Used() { lastUse = PR_Now(); }
nsRefPtr<gfxTextRun> textRun;
PRTime lastUse;
gfxTextRun* textRun;
PRTime lastUse;
~TextRunEntry() { delete textRun; }
};
typedef FontGroupAndStringT<nsAString, nsString> FontGroupAndString;
@ -149,9 +153,6 @@ protected:
typedef FontGroupAndStringHashKeyT<FontGroupAndString> FontGroupAndStringHashKey;
typedef FontGroupAndStringHashKeyT<FontGroupAndCString> FontGroupAndCStringHashKey;
//nsRefPtrHashtable<FontGroupAndStringHashKey, gfxTextRun> mHashTableUTF16;
//nsRefPtrHashtable<FontGroupAndCStringHashKey, gfxTextRun> mHashTableASCII;
nsClassHashtable<FontGroupAndStringHashKey, TextRunEntry> mHashTableUTF16;
nsClassHashtable<FontGroupAndCStringHashKey, TextRunEntry> mHashTableASCII;

View File

@ -418,8 +418,14 @@ public:
gfxWindowsFontGroup(const nsAString& aFamilies, const gfxFontStyle* aStyle);
virtual ~gfxWindowsFontGroup();
virtual gfxTextRun *MakeTextRun(const nsAString& aString);
virtual gfxTextRun *MakeTextRun(const nsACString& aString);
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle) {
NS_ERROR("NOT IMPLEMENTED");
return nsnull;
}
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams);
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams);
const nsACString& GetGenericFamily() const {
return mGenericFamily;
@ -476,7 +482,7 @@ private:
*
**********************************************************************/
class THEBES_API gfxWindowsTextRun : public gfxTextRun {
class THEBES_API gfxWindowsTextRun {
public:
gfxWindowsTextRun(const nsAString& aString, gfxWindowsFontGroup *aFontGroup);
gfxWindowsTextRun(const nsACString& aString, gfxWindowsFontGroup *aFontGroup);
@ -487,6 +493,9 @@ public:
virtual void SetSpacing(const nsTArray<gfxFloat>& spacingArray);
virtual const nsTArray<gfxFloat> *const GetSpacing() const;
void SetRightToLeft(PRBool aIsRTL) { mIsRTL = aIsRTL; }
PRBool IsRightToLeft() { return mIsRTL; }
private:
double MeasureOrDrawFast(gfxContext *aContext, PRBool aDraw, gfxPoint pt);
@ -500,8 +509,9 @@ private:
nsString mString;
nsCString mCString;
const PRBool mIsASCII;
const PRPackedBool mIsASCII;
PRPackedBool mIsRTL;
nsRefPtr<gfxWindowsFont> mFallbackFont;
/* cached values */

View File

@ -26,8 +26,10 @@ CPPSRCS = \
gfxContext.cpp \
gfxImageSurface.cpp \
gfxFont.cpp \
gfxPlatform.cpp \
gfxRect.cpp \
gfxSkipChars.cpp \
gfxTextRunCache.cpp \
gfxPlatform.cpp \
$(NULL)
ifdef MOZ_TREE_CAIRO

View File

@ -307,10 +307,156 @@ gfxAtsuiFontGroup::~gfxAtsuiFontGroup()
ATSUDisposeFontFallbacks(mFallbacks);
}
gfxTextRun*
gfxAtsuiFontGroup::MakeTextRun(const nsAString& aString)
class gfxWrapperTextRun : public gfxTextRun {
public:
gfxWrapperTextRun(gfxAtsuiFontGroup *aGroup,
const PRUint8* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams)
: gfxTextRun(aParams, PR_TRUE),
mContext(aParams->mContext),
mInner(NS_ConvertASCIItoUTF16(nsDependentCSubstring(reinterpret_cast<const char*>(aString),
reinterpret_cast<const char*>(aString + aLength))),
aGroup),
mLength(aLength)
{
mInner.SetRightToLeft(IsRightToLeft());
}
gfxWrapperTextRun(gfxAtsuiFontGroup *aGroup,
const PRUnichar* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams)
: gfxTextRun(aParams, PR_TRUE), mContext(aParams->mContext),
mInner(nsDependentSubstring(aString, aString + aLength), aGroup),
mLength(aLength)
{
mInner.SetRightToLeft(IsRightToLeft());
}
~gfxWrapperTextRun() {}
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
PRUint8* aFlags)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual PRUint8 GetCharFlags(PRUint32 aOffset)
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
virtual PRUint32 GetLength()
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore)
{ NS_ERROR("NOT IMPLEMENTED"); return PR_FALSE; }
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual void DrawSpecialString(gfxContext* aContext, gfxPoint aPt,
SpecialString aString)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
PRBool aTightBoundingBox,
PropertyProvider* aBreakProvider)
{ NS_ERROR("NOT IMPLEMENTED"); return Metrics(); }
virtual Metrics MeasureTextSpecialString(SpecialString aString,
PRBool aTightBoundingBox)
{ NS_ERROR("NOT IMPLEMENTED"); return Metrics(); }
virtual gfxFloat GetAdvanceWidthSpecialString(SpecialString aString)
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
virtual gfxFont::Metrics GetDecorationMetrics()
{ NS_ERROR("NOT IMPLEMENTED"); return gfxFont::Metrics(); }
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
TextProvider* aProvider,
gfxFloat* aAdvanceWidthDelta)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool aLineBreakBefore, gfxFloat aWidth,
PropertyProvider* aProvider,
PRBool aSuppressInitialBreak,
Metrics* aMetrics, PRBool aTightBoundingBox,
PRBool* aUsedHyphenation,
PRUint32* aLastBreak)
{ NS_ERROR("NOT IMPLEMENTED"); return 0; }
virtual void FlushSpacingCache(PRUint32 aStart)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth);
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider);
virtual void SetContext(gfxContext* aContext) { mContext = aContext; }
private:
gfxContext* mContext;
gfxAtsuiTextRun mInner;
PRUint32 mLength;
void SetupSpacingFromProvider(PropertyProvider* aProvider);
};
#define ROUND(x) floor((x) + 0.5)
void
gfxWrapperTextRun::SetupSpacingFromProvider(PropertyProvider* aProvider)
{
return new gfxAtsuiTextRun(aString, this);
if (!(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
return;
NS_ASSERTION(mFlags & gfxTextRunFactory::TEXT_ABSOLUTE_SPACING,
"Can't handle relative spacing");
nsAutoTArray<PropertyProvider::Spacing,200> spacing;
spacing.AppendElements(mLength);
aProvider->GetSpacing(0, mLength, spacing.Elements());
nsTArray<gfxFloat> spaceArray;
PRUint32 i;
gfxFloat offset = 0;
for (i = 0; i < mLength; ++i) {
NS_ASSERTION(spacing.Elements()[i].mBefore == 0,
"Can't handle before-spacing!");
gfxFloat nextOffset = offset + spacing.Elements()[i].mAfter/mPixelsToAppUnits;
spaceArray.AppendElement(ROUND(nextOffset) - ROUND(offset));
offset = nextOffset;
}
mInner.SetSpacing(spaceArray);
}
void
gfxWrapperTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth)
{
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
SetupSpacingFromProvider(aBreakProvider);
gfxPoint pt(aPt.x/mPixelsToAppUnits, aPt.y/mPixelsToAppUnits);
return mInner.Draw(mContext, pt);
}
gfxFloat
gfxWrapperTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider)
{
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
SetupSpacingFromProvider(aBreakProvider);
return mInner.Measure(mContext)*mPixelsToAppUnits;
}
gfxTextRun *
gfxAtsuiFontGroup::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams)
{
return new gfxWrapperTextRun(this, aString, aLength, aParams);
}
gfxTextRun *
gfxAtsuiFontGroup::MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams)
{
return new gfxWrapperTextRun(this, aString, aLength, aParams);
}
gfxAtsuiFont*

View File

@ -630,14 +630,6 @@ gfxContext::Mask(gfxASurface *surface, gfxPoint offset)
cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
}
// fonts?
void
gfxContext::DrawTextRun(gfxTextRun *text, gfxPoint pt)
{
if (text)
text->Draw(this, pt);
}
void
gfxContext::Paint(gfxFloat alpha)
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan (rocallahan@novell.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxRect.h"
gfxRect gfxRect::Intersect(const gfxRect& aRect) const
{
gfxRect result(0,0,0,0);
gfxFloat x = PR_MAX(aRect.X(), X());
gfxFloat xmost = PR_MIN(aRect.XMost(), XMost());
if (x >= xmost)
return result;
gfxFloat y = PR_MAX(aRect.Y(), Y());
gfxFloat ymost = PR_MIN(aRect.YMost(), YMost());
if (y >= ymost)
return result;
result = gfxRect(x, y, xmost - x, ymost - y);
return result;
}
gfxRect gfxRect::Union(const gfxRect& aRect) const
{
gfxRect result(0,0,0,0);
if (!aRect.IsEmpty() && !IsEmpty()) {
gfxFloat x = PR_MIN(aRect.X(), X());
gfxFloat xmost = PR_MAX(aRect.XMost(), XMost());
gfxFloat y = PR_MIN(aRect.Y(), Y());
gfxFloat ymost = PR_MAX(aRect.YMost(), YMost());
result = gfxRect(x, y, xmost - x, ymost - y);
}
return result;
}

View File

@ -0,0 +1,243 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxSkipChars.h"
#include <stdlib.h>
#define SHORTCUT_FREQUENCY 256
// Even numbered list entries are "keep" entries
static PRBool
IsKeepEntry(PRUint32 aEntry)
{
return !(aEntry & 1);
}
void
gfxSkipChars::BuildShortcuts()
{
if (!mList || mCharCount < SHORTCUT_FREQUENCY)
return;
mShortcuts = new Shortcut[mCharCount/SHORTCUT_FREQUENCY];
if (!mShortcuts)
return;
PRUint32 i;
PRUint32 nextShortcutIndex = 0;
PRUint32 originalCharOffset = 0;
PRUint32 skippedCharOffset = 0;
for (i = 0; i < mListLength; ++i) {
PRUint8 len = mList[i];
while (originalCharOffset + len > (nextShortcutIndex + 1)*SHORTCUT_FREQUENCY) {
mShortcuts[nextShortcutIndex] =
Shortcut(i, originalCharOffset, skippedCharOffset);
++nextShortcutIndex;
}
originalCharOffset += len;
if (IsKeepEntry(i)) {
skippedCharOffset += len;
}
}
}
void
gfxSkipCharsIterator::SetOffsets(PRUint32 aOffset, PRBool aInOriginalString)
{
if (mSkipChars->mListLength == 0) {
// Special case: all chars kept, original and stripped strings are equal
if (aOffset < 0) {
aOffset = 0;
} else if (aOffset > mSkipChars->mCharCount) {
aOffset = mSkipChars->mCharCount;
}
mOriginalStringOffset = mSkippedStringOffset = aOffset;
return;
}
if (aOffset == 0) {
// Start from the beginning of the string.
mSkippedStringOffset = 0;
mOriginalStringOffset = 0;
mListPrefixLength = 0;
mListPrefixKeepCharCount = 0;
mListPrefixCharCount = 0;
if (aInOriginalString) {
// Nothing more to do!
return;
}
}
if (aInOriginalString && mSkipChars->mShortcuts &&
abs(aOffset - mListPrefixCharCount) > SHORTCUT_FREQUENCY) {
// Take a shortcut. This makes SetOffsets(..., PR_TRUE) O(1) by bounding
// the iterations in the loop below to at most SHORTCUT_FREQUENCY iterations
PRUint32 shortcutIndex = aOffset/SHORTCUT_FREQUENCY;
if (shortcutIndex == 0) {
mListPrefixLength = 0;
mListPrefixKeepCharCount = 0;
mListPrefixCharCount = 0;
} else {
const gfxSkipChars::Shortcut& shortcut = mSkipChars->mShortcuts[shortcutIndex - 1];
mListPrefixLength = shortcut.mListPrefixLength;
mListPrefixKeepCharCount = shortcut.mListPrefixKeepCharCount;
mListPrefixCharCount = shortcut.mListPrefixCharCount;
}
}
PRInt32 currentRunLength = mSkipChars->mList[mListPrefixLength];
for (;;) {
// See if aOffset is in the string segment described by
// mSkipChars->mList[mListPrefixLength]
PRUint32 segmentOffset = aInOriginalString ? mListPrefixCharCount : mListPrefixKeepCharCount;
if ((aInOriginalString || IsKeepEntry(mListPrefixLength)) &&
aOffset >= segmentOffset && aOffset < segmentOffset + currentRunLength) {
PRInt32 offsetInSegment = aOffset - segmentOffset;
mOriginalStringOffset = mListPrefixCharCount + offsetInSegment;
mSkippedStringOffset = mListPrefixKeepCharCount;
if (IsKeepEntry(mListPrefixLength)) {
mSkippedStringOffset += offsetInSegment;
}
return;
}
if (aOffset < segmentOffset) {
// We need to move backwards
if (mListPrefixLength <= 0) {
// nowhere to go backwards
mOriginalStringOffset = mSkippedStringOffset = 0;
return;
}
// Go backwards one segment and restore invariants
--mListPrefixLength;
currentRunLength = mSkipChars->mList[mListPrefixLength];
mListPrefixCharCount -= currentRunLength;
if (IsKeepEntry(mListPrefixLength)) {
mListPrefixKeepCharCount -= currentRunLength;
}
} else {
// We need to move forwards
if (mListPrefixLength >= mSkipChars->mListLength - 1) {
// nowhere to go forwards
mOriginalStringOffset = mListPrefixCharCount + currentRunLength;
mSkippedStringOffset = mListPrefixKeepCharCount;
if (IsKeepEntry(mListPrefixLength)) {
mSkippedStringOffset += currentRunLength;
}
return;
}
// Go forwards one segment and restore invariants
mListPrefixCharCount += currentRunLength;
if (IsKeepEntry(mListPrefixLength)) {
mListPrefixKeepCharCount += currentRunLength;
}
++mListPrefixLength;
currentRunLength = mSkipChars->mList[mListPrefixLength];
}
}
}
PRBool
gfxSkipCharsIterator::IsOriginalCharSkipped(PRInt32* aRunLength) const
{
if (mSkipChars->mListLength == 0) {
if (aRunLength) {
*aRunLength = mSkipChars->mCharCount - mOriginalStringOffset;
}
return mSkipChars->mCharCount == PRUint32(mOriginalStringOffset);
}
// figure out which segment we're in
PRUint32 currentRunLength = mSkipChars->mList[mListPrefixLength];
if (mListPrefixLength >= mSkipChars->mListLength - 1 &&
PRUint32(mOriginalStringOffset) >= mListPrefixCharCount + currentRunLength) {
NS_ASSERTION(mListPrefixLength == mSkipChars->mListLength - 1 &&
PRUint32(mOriginalStringOffset) == mListPrefixCharCount + currentRunLength,
"Overran end of string");
// We're at the end of the string
if (aRunLength) {
*aRunLength = 0;
}
return PR_TRUE;
}
PRBool isSkipped = !IsKeepEntry(mListPrefixLength);
if (aRunLength) {
// Long runs of all-skipped or all-kept characters will be encoded as
// sequences of 255, 0, 255, 0 etc. Compute the maximum run length by skipping
// over zero entries.
for (PRUint32 i = mListPrefixLength + 2; i < mSkipChars->mListLength; ++i) {
if (isSkipped == !IsKeepEntry(i) && mSkipChars->mList[i - 1] == 0) {
currentRunLength += mSkipChars->mList[i];
}
}
*aRunLength = currentRunLength;
}
return isSkipped;
}
void
gfxSkipCharsBuilder::FlushRun()
{
NS_ASSERTION((mBuffer.Length() & 1) == mRunSkipped,
"out of sync?");
// Fill in buffer entries starting at mBufferLength, as many as necessary
PRUint32 charCount = mRunCharCount;
for (;;) {
PRUint32 chars = PR_MIN(255, charCount);
if (!mBuffer.AppendElement(chars)) {
mInErrorState = PR_TRUE;
return;
}
charCount -= chars;
if (charCount == 0)
break;
if (!mBuffer.AppendElement(0)) {
mInErrorState = PR_TRUE;
return;
}
}
NS_ASSERTION(mCharCount + mRunCharCount >= mCharCount,
"String length overflow");
mCharCount += mRunCharCount;
mRunCharCount = 0;
mRunSkipped = !mRunSkipped;
}

View File

@ -63,53 +63,128 @@ gfxTextRunCache::GetCache()
return mGlobalCache;
}
gfxTextRun*
gfxTextRunCache::GetOrMakeTextRun (gfxFontGroup *aFontGroup, const nsAString& aString)
static PRUint32 ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing)
{
if (gDisableCache)
return aFontGroup->MakeTextRun(aString);
PRUint32 flags = gfxTextRunFactory::TEXT_HAS_SURROGATES;
if (aIsRTL) {
flags |= gfxTextRunFactory::TEXT_IS_RTL;
}
if (aEnableSpacing) {
flags |= gfxTextRunFactory::TEXT_ENABLE_SPACING |
gfxTextRunFactory::TEXT_ABSOLUTE_SPACING |
gfxTextRunFactory::TEXT_ENABLE_NEGATIVE_SPACING;
}
return flags;
}
// Evict first, to make sure that the textrun we return is live.
EvictUTF16();
gfxTextRun*
gfxTextRunCache::GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
const PRUnichar *aString, PRUint32 aLength, gfxFloat aDevToApp,
PRBool aIsRTL, PRBool aEnableSpacing, PRBool *aCallerOwns)
{
gfxSkipChars skipChars;
// Choose pessimistic flags since we don't want to bother analyzing the string
gfxTextRunFactory::Parameters params =
{ aContext, nsnull, nsnull, &skipChars, nsnull, 0, aDevToApp,
ComputeFlags(aIsRTL, aEnableSpacing) };
gfxTextRun *tr;
TextRunEntry *entry;
FontGroupAndString key(aFontGroup, &aString);
gfxTextRun* tr = nsnull;
// Don't cache textruns that use spacing
if (!gDisableCache && !aEnableSpacing) {
// Evict first, to make sure that the textrun we return is live.
EvictUTF16();
gfxTextRun *tr;
TextRunEntry *entry;
nsDependentSubstring keyStr(aString, aString + aLength);
FontGroupAndString key(aFontGroup, &keyStr);
if (mHashTableUTF16.Get(key, &entry)) {
entry->Used();
tr = entry->textRun.get();
if (mHashTableUTF16.Get(key, &entry)) {
gfxTextRun *cachedTR = entry->textRun;
// Check that this matches what we wanted. If it doesn't, we leave
// this cache entry alone and return a fresh, caller-owned textrun
// below.
if (cachedTR->GetPixelsToAppUnits() == aDevToApp &&
cachedTR->IsRightToLeft() == aIsRTL) {
entry->Used();
tr = cachedTR;
tr->SetContext(aContext);
}
} else {
tr = aFontGroup->MakeTextRun(aString, aLength, &params);
entry = new TextRunEntry(tr);
key.Realize();
mHashTableUTF16.Put(key, entry);
}
}
if (tr) {
*aCallerOwns = PR_FALSE;
} else {
tr = aFontGroup->MakeTextRun(aString);
entry = new TextRunEntry(tr);
key.Realize();
mHashTableUTF16.Put(key, entry);
// Textrun is not in the cache for some reason.
*aCallerOwns = PR_TRUE;
tr = aFontGroup->MakeTextRun(aString, aLength, &params);
}
if (tr) {
// We don't want to have to reconstruct the string
tr->RememberText(aString, aLength);
}
return tr;
}
gfxTextRun*
gfxTextRunCache::GetOrMakeTextRun (gfxFontGroup *aFontGroup, const nsACString& aString)
gfxTextRunCache::GetOrMakeTextRun (gfxContext* aContext, gfxFontGroup *aFontGroup,
const char *aString, PRUint32 aLength, gfxFloat aDevToApp,
PRBool aIsRTL, PRBool aEnableSpacing, PRBool *aCallerOwns)
{
if (gDisableCache)
return aFontGroup->MakeTextRun(aString);
gfxSkipChars skipChars;
// Choose pessimistic flags since we don't want to bother analyzing the string
gfxTextRunFactory::Parameters params =
{ aContext, nsnull, nsnull, &skipChars, nsnull, 0, aDevToApp,
ComputeFlags(aIsRTL, aEnableSpacing) };
const PRUint8* str = NS_REINTERPRET_CAST(const PRUint8*, aString);
// Evict first, to make sure that the textrun we return is live.
EvictASCII();
gfxTextRun* tr = nsnull;
// Don't cache textruns that use spacing
if (!gDisableCache && !aEnableSpacing) {
// Evict first, to make sure that the textrun we return is live.
EvictASCII();
gfxTextRun *tr;
TextRunEntry *entry;
nsDependentCSubstring keyStr(aString, aString + aLength);
FontGroupAndCString key(aFontGroup, &keyStr);
gfxTextRun *tr;
TextRunEntry *entry;
FontGroupAndCString key(aFontGroup, &aString);
if (mHashTableASCII.Get(key, &entry)) {
gfxTextRun *cachedTR = entry->textRun;
// Check that this matches what we wanted. If it doesn't, we leave
// this cache entry alone and return a fresh, caller-owned textrun
// below.
if (cachedTR->GetPixelsToAppUnits() == aDevToApp &&
cachedTR->IsRightToLeft() == aIsRTL) {
entry->Used();
tr = cachedTR;
tr->SetContext(aContext);
}
} else {
tr = aFontGroup->MakeTextRun(str, aLength, &params);
entry = new TextRunEntry(tr);
key.Realize();
mHashTableASCII.Put(key, entry);
}
}
if (mHashTableASCII.Get(key, &entry)) {
entry->Used();
tr = entry->textRun.get();
if (tr) {
*aCallerOwns = PR_FALSE;
} else {
tr = aFontGroup->MakeTextRun(aString);
entry = new TextRunEntry(tr);
key.Realize();
mHashTableASCII.Put(key, entry);
// Textrun is not in the cache for some reason.
*aCallerOwns = PR_TRUE;
tr = aFontGroup->MakeTextRun(str, aLength, &params);
}
if (tr) {
// We don't want to have to reconstruct the string
tr->RememberText(str, aLength);
}
return tr;

View File

@ -481,24 +481,153 @@ gfxWindowsFontGroup::~gfxWindowsFontGroup()
}
gfxTextRun *
gfxWindowsFontGroup::MakeTextRun(const nsAString& aString)
{
if (aString.IsEmpty()) {
NS_WARNING("It is illegal to create a gfxTextRun with empty strings");
return nsnull;
class gfxWrapperTextRun : public gfxTextRun {
public:
gfxWrapperTextRun(gfxWindowsFontGroup *aGroup,
const PRUint8* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams)
: gfxTextRun(aParams, PR_TRUE), mContext(aParams->mContext),
mInner(nsDependentCSubstring(reinterpret_cast<const char*>(aString),
reinterpret_cast<const char*>(aString + aLength)),
aGroup),
mLength(aLength)
{
mInner.SetRightToLeft(IsRightToLeft());
}
return new gfxWindowsTextRun(aString, this);
gfxWrapperTextRun(gfxWindowsFontGroup *aGroup,
const PRUnichar* aString, PRUint32 aLength,
gfxTextRunFactory::Parameters* aParams)
: gfxTextRun(aParams, PR_TRUE), mContext(aParams->mContext),
mInner(nsDependentSubstring(aString, aString + aLength), aGroup),
mLength(aLength)
{
mInner.SetRightToLeft(IsRightToLeft());
}
~gfxWrapperTextRun() {}
virtual void GetCharFlags(PRUint32 aStart, PRUint32 aLength,
PRUint8* aFlags)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual PRUint8 GetCharFlags(PRUint32 aOffset)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual PRUint32 GetLength()
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual void DrawToPath(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual void DrawSpecialString(gfxContext* aContext, gfxPoint aPt,
SpecialString aString)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual Metrics MeasureText(PRUint32 aStart, PRUint32 aLength,
PRBool aTightBoundingBox,
PropertyProvider* aBreakProvider)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual Metrics MeasureTextSpecialString(SpecialString aString,
PRBool aTightBoundingBox)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual gfxFloat GetAdvanceWidthSpecialString(SpecialString aString)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual gfxFont::Metrics GetDecorationMetrics()
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual void SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
TextProvider* aProvider,
gfxFloat* aAdvanceWidthDelta)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual PRUint32 BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
PRBool aLineBreakBefore, gfxFloat aWidth,
PropertyProvider* aProvider,
PRBool aSuppressInitialBreak,
Metrics* aMetrics, PRBool aTightBoundingBox,
PRBool* aUsedHyphenation,
PRUint32* aLastBreak)
{ NS_ERROR("NOT IMPLEMENTED"); for (;;) ; }
virtual void FlushSpacingCache(PRUint32 aStart)
{ NS_ERROR("NOT IMPLEMENTED"); }
virtual void Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth);
virtual gfxFloat GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider);
virtual void SetContext(gfxContext* aContext) { mContext = aContext; }
private:
gfxContext* mContext;
gfxWindowsTextRun mInner;
PRUint32 mLength;
void SetupSpacingFromProvider(PropertyProvider* aProvider);
};
void
gfxWrapperTextRun::SetupSpacingFromProvider(PropertyProvider* aProvider)
{
if (!(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
return;
NS_ASSERTION(mFlags & gfxTextRunFactory::TEXT_ABSOLUTE_SPACING,
"Can't handle relative spacing");
nsAutoTArray<PropertyProvider::Spacing,200> spacing;
spacing.AppendElements(mLength);
aProvider->GetSpacing(0, mLength, spacing.Elements());
nsTArray<gfxFloat> spaceArray;
PRUint32 i;
gfxFloat offset = 0;
for (i = 0; i < mLength; ++i) {
NS_ASSERTION(spacing.Elements()[i].mBefore == 0,
"Can't handle before-spacing!");
gfxFloat nextOffset = offset + spacing.Elements()[i].mAfter/mPixelsToAppUnits;
spaceArray.AppendElement(ROUND(nextOffset) - ROUND(offset));
offset = nextOffset;
}
mInner.SetSpacing(spaceArray);
}
void
gfxWrapperTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
PRUint32 aStart, PRUint32 aLength,
const gfxRect* aDirtyRect,
PropertyProvider* aBreakProvider,
gfxFloat* aAdvanceWidth)
{
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
SetupSpacingFromProvider(aBreakProvider);
gfxPoint pt(aPt.x/mPixelsToAppUnits, aPt.y/mPixelsToAppUnits);
return mInner.Draw(mContext, pt);
}
gfxFloat
gfxWrapperTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
PropertyProvider* aBreakProvider)
{
NS_ASSERTION(aStart == 0 && aLength == mLength, "Can't handle substrings");
SetupSpacingFromProvider(aBreakProvider);
return mInner.Measure(mContext)*mPixelsToAppUnits;
}
gfxTextRun *
gfxWindowsFontGroup::MakeTextRun(const nsACString& aString)
gfxWindowsFontGroup::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams)
{
if (aString.IsEmpty()) {
NS_WARNING("It is illegal to create a gfxTextRun with empty strings");
return nsnull;
}
return new gfxWindowsTextRun(aString, this);
return new gfxWrapperTextRun(this, aString, aLength, aParams);
}
gfxTextRun *
gfxWindowsFontGroup::MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams)
{
return new gfxWrapperTextRun(this, aString, aLength, aParams);
}
/**********************************************************************

View File

@ -43,11 +43,10 @@
#define NS_LINEBREAKER_NEED_MORE_TEXT -1
// {E86B3375-BF89-11d2-B3AF-00805F8A6670}
// {c3d9f25f-7cea-4a76-a08f-05c431353448}
#define NS_ILINEBREAKER_IID \
{ 0xe86b3375, 0xbf89, 0x11d2, \
{ 0xb3, 0xaf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
{ 0xc3d9f25f, 0x7cea, 0x4a76, \
{ 0xa0, 0x8f, 0x05, 0xc4, 0x31, 0x35, 0x34, 0x48 } }
class nsILineBreaker : public nsISupports
{
@ -63,6 +62,14 @@ public:
virtual PRInt32 Prev( const PRUnichar* aText, PRUint32 aLen,
PRUint32 aPos) = 0;
// Call this on a word with whitespace at either end. We will apply JISx4501
// rules to find breaks inside the word. aBreakBefore is set to the break-
// before status of each character; aBreakBefore[0] will always be false
// because we never return a break before the first character.
// aLength is the length of the aText array and also the length of the aBreakBefore
// output array.
virtual void GetJISx4051Breaks(const PRUnichar* aText, PRUint32 aLength,
PRPackedBool* aBreakBefore) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsILineBreaker, NS_ILINEBREAKER_IID)

View File

@ -47,7 +47,6 @@
#include "rulebrk.h"
#include "nsUnicharUtils.h"
/*
Simplification of Pair Table in JIS X 4051
@ -241,7 +240,7 @@ IS_SPACE(PRUnichar u)
return ((u) == 0x0020 || (u) == 0x0009 || (u) == 0x000a || (u) == 0x000d || (u)==0x200b);
}
PRInt8 nsJISx4051LineBreaker::GetClass(PRUnichar u)
static PRInt8 GetClass(PRUnichar u)
{
PRUint16 h = u & 0xFF00;
PRUint16 l = u & 0x00ff;
@ -332,7 +331,7 @@ PRInt8 nsJISx4051LineBreaker::GetClass(PRUnichar u)
return c;
}
PRBool nsJISx4051LineBreaker::GetPair(PRInt8 c1, PRInt8 c2)
static PRBool GetPair(PRInt8 c1, PRInt8 c2)
{
NS_ASSERTION( c1 < MAX_CLASSES ,"illegal classes 1");
NS_ASSERTION( c2 < MAX_CLASSES ,"illegal classes 2");
@ -361,9 +360,8 @@ NS_IMPL_ISUPPORTS1(nsJISx4051LineBreaker, nsILineBreaker)
#define CHARACTER_CLASS 8 // JIS x4051 class 18 is now map to simplified class 8
#define IS_ASCII_DIGIT(u) (0x0030 <= (u) && (u) <= 0x0039)
PRInt8 nsJISx4051LineBreaker::ContextualAnalysis(
PRUnichar prev, PRUnichar cur, PRUnichar next
)
static PRInt8 ContextualAnalysis(
PRUnichar prev, PRUnichar cur, PRUnichar next)
{
if(U_COMMA == cur)
{
@ -393,7 +391,7 @@ PRInt8 nsJISx4051LineBreaker::ContextualAnalysis(
if(U_SPACE != next)
return CHARACTER_CLASS;
}
return this->GetClass(cur);
return GetClass(cur);
}
@ -435,18 +433,18 @@ ROUTE_CJK_BETWEEN:
PRInt8 c1, c2;
if(NEED_CONTEXTUAL_ANALYSIS(aText1[aTextLen1-1]))
c1 = this->ContextualAnalysis((aTextLen1>1)?aText1[aTextLen1-2]:0,
c1 = ContextualAnalysis((aTextLen1>1)?aText1[aTextLen1-2]:0,
aText1[aTextLen1-1],
aText2[0]);
else
c1 = this->GetClass(aText1[aTextLen1-1]);
c1 = GetClass(aText1[aTextLen1-1]);
if(NEED_CONTEXTUAL_ANALYSIS(aText2[0]))
c2 = this->ContextualAnalysis(aText1[aTextLen1-1],
aText2[0],
(aTextLen2>1)?aText2[1]:0);
c2 = ContextualAnalysis(aText1[aTextLen1-1],
aText2[0],
(aTextLen2>1)?aText2[1]:0);
else
c2 = this->GetClass(aText2[0]);
c2 = GetClass(aText2[0]);
/* Handle cases for THAI */
if((CLASS_THAI == c1) && (CLASS_THAI == c2))
@ -483,11 +481,11 @@ ROUTE_CJK_NEXT:
cur = aPos;
if(NEED_CONTEXTUAL_ANALYSIS(aText[cur]))
{
c1 = this->ContextualAnalysis((cur>0)?aText[cur-1]:0,
aText[cur],
(cur<(aLen-1)) ?aText[cur+1]:0);
c1 = ContextualAnalysis((cur>0)?aText[cur-1]:0,
aText[cur],
(cur<(aLen-1)) ?aText[cur+1]:0);
} else {
c1 = this->GetClass(aText[cur]);
c1 = GetClass(aText[cur]);
}
if(CLASS_THAI == c1)
@ -497,11 +495,11 @@ ROUTE_CJK_NEXT:
{
if(NEED_CONTEXTUAL_ANALYSIS(aText[cur]))
{
c2= this->ContextualAnalysis((cur>0)?aText[cur-1]:0,
aText[cur],
(cur<(aLen-1)) ?aText[cur+1]:0);
c2 = ContextualAnalysis((cur>0)?aText[cur-1]:0,
aText[cur],
(cur<(aLen-1)) ?aText[cur+1]:0);
} else {
c2 = this->GetClass(aText[cur]);
c2 = GetClass(aText[cur]);
}
if(GetPair(c1, c2)) {
@ -539,11 +537,11 @@ ROUTE_CJK_PREV:
PRInt8 c1, c2;
if(NEED_CONTEXTUAL_ANALYSIS(aText[cur-1]))
{
c2 = this->ContextualAnalysis(((cur-1)>0)?aText[cur-2]:0,
aText[cur-1],
(cur<aLen) ?aText[cur]:0);
c2 = ContextualAnalysis(((cur-1)>0)?aText[cur-2]:0,
aText[cur-1],
(cur<aLen) ?aText[cur]:0);
} else {
c2 = this->GetClass(aText[cur-1]);
c2 = GetClass(aText[cur-1]);
}
// To Do:
//
@ -553,11 +551,11 @@ ROUTE_CJK_PREV:
{
if(NEED_CONTEXTUAL_ANALYSIS(aText[cur-1]))
{
c1= this->ContextualAnalysis(((cur-1)>0)?aText[cur-2]:0,
aText[cur-1],
(cur<aLen) ?aText[cur]:0);
c1 = ContextualAnalysis(((cur-1)>0)?aText[cur-2]:0,
aText[cur-1],
(cur<aLen) ?aText[cur]:0);
} else {
c1 = this->GetClass(aText[cur-1]);
c1 = GetClass(aText[cur-1]);
}
if(GetPair(c1, c2)) {
@ -568,3 +566,36 @@ ROUTE_CJK_PREV:
return NS_LINEBREAKER_NEED_MORE_TEXT; // Need more text
}
void
nsJISx4051LineBreaker::GetJISx4051Breaks(const PRUnichar* aChars, PRUint32 aLength,
PRPackedBool* aBreakBefore)
{
PRUint32 cur;
PRInt8 lastClass = -1;
for (cur = 0; cur < aLength; ++cur) {
PRUnichar ch = aChars[cur];
PRInt8 cl;
if (NEED_CONTEXTUAL_ANALYSIS(ch)) {
cl = ContextualAnalysis(cur > 0 ? aChars[cur - 1] : 0,
ch,
cur + 1 < aLength ? aChars[cur + 1] : 0);
} else {
cl = GetClass(ch);
}
PRBool allowBreak;
if (cur > 0) {
if (CLASS_THAI == lastClass && CLASS_THAI == cl) {
allowBreak = 0 == TrbWordBreakPos(aChars, cur, aChars + cur, aLength - cur);
} else {
allowBreak = GetPair(lastClass, cl);
}
} else {
allowBreak = PR_FALSE;
}
aBreakBefore[cur] = allowBreak;
lastClass = cl;
}
}

View File

@ -55,12 +55,8 @@ public:
PRInt32 Prev( const PRUnichar* aText, PRUint32 aLen, PRUint32 aPos);
protected:
PRInt8 GetClass(PRUnichar u);
PRInt8 ContextualAnalysis(PRUnichar prev, PRUnichar cur, PRUnichar next );
PRBool GetPair(PRInt8 c1, PRInt8 c2);
virtual void GetJISx4051Breaks(const PRUnichar* aText, PRUint32 aLength,
PRPackedBool* aBreakBefore);
};
#endif /* nsJISx4501LineBreaker_h__ */

View File

@ -58,6 +58,7 @@
#include "nsDisplayList.h"
#include "nsRegion.h"
#include "nsFrameManager.h"
#include "nsBlockFrame.h"
#ifdef MOZ_SVG_FOREIGNOBJECT
#include "nsSVGForeignObjectFrame.h"
@ -998,6 +999,37 @@ nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
*aFontMetrics);
}
nsIFrame*
nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
{
nsIFrame* result = aDescendantFrame;
while (result) {
nsIFrame* parent = result->GetParent();
if (parent == aParent) {
break;
}
// The frame is not an immediate child of aParent so walk up another level
result = parent;
}
return result;
}
nsBlockFrame*
nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame)
{
nsIFrame* nextAncestor;
for (nextAncestor = aFrame->GetParent(); nextAncestor;
nextAncestor = nextAncestor->GetParent()) {
nsBlockFrame* block;
if (NS_SUCCEEDED(nextAncestor->QueryInterface(kBlockFrameCID, (void**)&block)))
return block;
}
return nsnull;
}
nsIFrame*
nsLayoutUtils::GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
nsIFrame* aFrame)

View File

@ -56,6 +56,8 @@ class nsIFontMetrics;
#include "nsStyleSet.h"
#include "nsIView.h"
class nsBlockFrame;
/**
* nsLayoutUtils is a namespace class used for various helper
* functions that are useful in multiple places in layout. The goal
@ -432,6 +434,18 @@ public:
static nsresult GetFontMetricsForFrame(nsIFrame* aFrame,
nsIFontMetrics** aFontMetrics);
/**
* Find the immediate child of aParent whose frame subtree contains
* aDescendantFrame. Returns null if aDescendantFrame is not a descendant
* of aParent.
*/
static nsIFrame* FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame);
/**
* Find the nearest ancestor that's a block
*/
static nsBlockFrame* FindNearestBlockAncestor(nsIFrame* aFrame);
/**
* If aFrame is an out of flow frame, return its placeholder, otherwise
* return its parent.

View File

@ -154,7 +154,6 @@ CPPSRCS = \
nsSpaceManager.cpp \
nsSpacerFrame.cpp \
nsSplittableFrame.cpp \
nsTextFrame.cpp \
nsTextTransformer.cpp \
nsViewportFrame.cpp \
$(NULL)
@ -165,6 +164,21 @@ CPPSRCS += \
$(NULL)
endif
# set this to 1 to enable the new text frame
MOZ_ENABLE_NEW_TEXT_FRAME =
ifdef MOZ_ENABLE_NEW_TEXT_FRAME
CPPSRCS += \
nsTextFrameThebes.cpp \
nsTextRunTransformations.cpp \
nsTextFrameUtils.cpp \
$(NULL)
else
CPPSRCS += \
nsTextFrame.cpp \
$(NULL)
endif
RESOURCES_HTML = \
$(srcdir)/gopher-audio.gif \
$(srcdir)/gopher-binary.gif \

View File

@ -2421,20 +2421,11 @@ nsBlockFrame::AttributeChanged(PRInt32 aNameSpaceID,
else if (nsGkAtoms::value == aAttribute) {
const nsStyleDisplay* styleDisplay = GetStyleDisplay();
if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
nsIFrame* nextAncestor = mParent;
nsBlockFrame* blockParent = nsnull;
// Search for the closest ancestor that's a block frame. We
// make the assumption that all related list items share a
// common block parent.
// XXXldb I think that's a bad assumption.
while (nextAncestor) {
if (NS_OK == nextAncestor->QueryInterface(kBlockFrameCID,
(void**)&blockParent)) {
break;
}
nextAncestor = nextAncestor->GetParent();
}
nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
// Tell the enclosing block frame to renumber list items within
// itself
@ -3110,7 +3101,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
// no longer makes sense. Now we always allocate on the stack
nsLineLayout lineLayout(aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState);
&aState.mReflowState, &aLine);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
if (forceBreakInContent) {
lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
@ -3322,8 +3313,11 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
}
if ((lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK) &&
!aLineLayout.HaveForcedBreakPosition() && aLineLayout.NeedsBackup()) {
aLineLayout.NeedsBackup()) {
// We need to try backing up to before a text run
NS_ASSERTION(!aLineLayout.HaveForcedBreakPosition(),
"We shouldn't be backing up more than once! "
"Someone must have set a break opportunity beyond the available width");
PRInt32 offset;
nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset);
if (breakContent) {
@ -3828,7 +3822,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE;
}
aLineLayout.VerticalAlignLine(aLine);
aLineLayout.VerticalAlignLine();
// Our ascent is the ascent of our first line (but if this line is all
// whitespace we'll correct things in |ReflowBlockFrame|).
if (aLine == mLines.front()) {

View File

@ -255,7 +255,7 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
// only time that the first-letter-frame is not reflowing in a
// line context is when its floating.
nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
nsLineLayout ll(aPresContext, nsnull, &aReflowState);
nsLineLayout ll(aPresContext, nsnull, &aReflowState, nsnull);
ll.BeginLineReflow(0, 0, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE,
PR_FALSE, PR_TRUE);
rs.mLineLayout = &ll;

View File

@ -651,24 +651,6 @@ nsHTMLReflowState::CalculateHorizBorderPaddingMargin(nscoord aContainingBlockWid
margin.left + margin.right;
}
static nsIFrame*
FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame)
{
nsIFrame* result = aDescendantFrame;
while (result) {
nsIFrame* parent = result->GetParent();
if (parent == aParent) {
break;
}
// The frame is not an immediate child of aParent so walk up another level
result = parent;
}
return result;
}
/**
* Returns PR_TRUE iff a pre-order traversal of the normal child
* frames rooted at aFrame finds no non-empty frame before aDescendant.
@ -776,7 +758,8 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext,
NS_REINTERPRET_CAST(void**, &blockFrame)))) {
// We need the immediate child of the block frame, and that may not be
// the placeholder frame
nsIFrame *blockChild = FindImmediateChildOf(blockFrame, aPlaceholderFrame);
nsIFrame *blockChild =
nsLayoutUtils::FindChildContainingDescendant(blockFrame, aPlaceholderFrame);
nsBlockFrame::line_iterator lineBox = blockFrame->FindLineFor(blockChild);
// How we determine the hypothetical box depends on whether the element

View File

@ -914,7 +914,11 @@ nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
}
}
NS_ASSERTION(!aReflowState.mLineLayout->GetInFirstLine(),
"Nested first-line frames? BOGUS");
aReflowState.mLineLayout->SetInFirstLine(PR_TRUE);
rv = ReflowFrames(aPresContext, aReflowState, irs, aMetrics, aStatus);
aReflowState.mLineLayout->SetInFirstLine(PR_FALSE);
// Note: the line layout code will properly compute our overflow state for us

View File

@ -93,7 +93,8 @@
nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
nsSpaceManager* aSpaceManager,
const nsHTMLReflowState* aOuterReflowState)
const nsHTMLReflowState* aOuterReflowState,
const nsLineList::iterator* aLine)
: mPresContext(aPresContext),
mSpaceManager(aSpaceManager),
mBlockReflowState(aOuterReflowState),
@ -133,6 +134,11 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
mCurrentSpan = mRootSpan = nsnull;
mSpanDepth = 0;
if (aLine) {
SetFlag(LL_GOTLINEBOX, PR_TRUE);
mLineBox = *aLine;
}
mCompatMode = mPresContext->CompatibilityMode();
}
@ -808,7 +814,6 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
#endif // IBMBIDI
nsIAtom* frameType = aFrame->GetType();
PRInt32 savedOptionalBreakOffset;
nsIContent* savedOptionalBreakContent =
GetLastOptionalBreakPosition(&savedOptionalBreakOffset);
@ -1393,7 +1398,7 @@ PRBool IsPercentageAwareFrame(nsPresContext *aPresContext, nsIFrame *aFrame)
void
nsLineLayout::VerticalAlignLine(nsLineBox* aLineBox)
nsLineLayout::VerticalAlignLine()
{
// Synthesize a PerFrameData for the block frame
PerFrameData rootPFD;
@ -1402,7 +1407,6 @@ nsLineLayout::VerticalAlignLine(nsLineBox* aLineBox)
rootPFD.mAscent = 0;
rootPFD.mDescent = 0;
mRootSpan->mFrame = &rootPFD;
mLineBox = aLineBox;
// Partially place the children of the block frame. The baseline for
// this operation is set to zero so that the y coordinates for all
@ -1507,32 +1511,31 @@ nsLineLayout::VerticalAlignLine(nsLineBox* aLineBox)
}
// check to see if the frame is an inline replace element
// and if it is percent-aware. If so, mark the line.
if ((PR_FALSE==aLineBox->ResizeReflowOptimizationDisabled()) &&
if ((PR_FALSE==mLineBox->ResizeReflowOptimizationDisabled()) &&
pfd->mFrameType & NS_CSS_FRAME_TYPE_INLINE)
{
if (IsPercentageAwareFrame(mPresContext, pfd->mFrame))
aLineBox->DisableResizeReflowOptimization();
mLineBox->DisableResizeReflowOptimization();
}
}
// Fill in returned line-box and max-element-width data
aLineBox->mBounds.x = psd->mLeftEdge;
aLineBox->mBounds.y = mTopEdge;
aLineBox->mBounds.width = psd->mX - psd->mLeftEdge;
aLineBox->mBounds.height = lineHeight;
mLineBox->mBounds.x = psd->mLeftEdge;
mLineBox->mBounds.y = mTopEdge;
mLineBox->mBounds.width = psd->mX - psd->mLeftEdge;
mLineBox->mBounds.height = lineHeight;
mFinalLineHeight = lineHeight;
aLineBox->SetAscent(baselineY - mTopEdge);
mLineBox->SetAscent(baselineY - mTopEdge);
#ifdef NOISY_VERTICAL_ALIGN
printf(
" [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
aLineBox->mBounds.x, aLineBox->mBounds.y,
aLineBox->mBounds.width, aLineBox->mBounds.height,
mFinalLineHeight, aLineBox->GetAscent());
mLineBox->mBounds.x, mLineBox->mBounds.y,
mLineBox->mBounds.width, mLineBox->mBounds.height,
mFinalLineHeight, mLineBox->GetAscent());
#endif
// Undo root-span mFrame pointer to prevent brane damage later on...
mRootSpan->mFrame = nsnull;
mLineBox = nsnull;
}
void

View File

@ -66,7 +66,8 @@ class nsLineLayout {
public:
nsLineLayout(nsPresContext* aPresContext,
nsSpaceManager* aSpaceManager,
const nsHTMLReflowState* aOuterReflowState);
const nsHTMLReflowState* aOuterReflowState,
const nsLineList::iterator* aLine);
~nsLineLayout();
void Init(nsBlockReflowState* aState, nscoord aMinLineHeight,
@ -126,7 +127,7 @@ public:
PushFrame(aFrame);
}
void VerticalAlignLine(nsLineBox* aLineBox);
void VerticalAlignLine();
PRBool TrimTrailingWhiteSpace();
@ -161,7 +162,9 @@ protected:
#define LL_LINEENDSINSOFTBR 0x00000400
#define LL_NEEDBACKUP 0x00000800
#define LL_LASTTEXTFRAME_WRAPPINGENABLED 0x00001000
#define LL_LASTFLAG LL_LASTTEXTFRAME_WRAPPINGENABLED
#define LL_INFIRSTLINE 0x00002000
#define LL_GOTLINEBOX 0x00004000
#define LL_LASTFLAG LL_GOTLINEBOX
PRUint16 mFlags;
@ -274,7 +277,7 @@ public:
* @param aWrappingEnabled whether that text had word-wrapping enabled
* (white-space:normal or -moz-pre-wrap)
*/
nsIFrame* GetTrailingTextFrame(PRBool* aWrappingEnabled) {
nsIFrame* GetTrailingTextFrame(PRBool* aWrappingEnabled) const {
*aWrappingEnabled = GetFlag(LL_LASTTEXTFRAME_WRAPPINGENABLED);
return mTrailingTextFrame;
}
@ -298,6 +301,14 @@ public:
mFirstLetterFrame = aFrame;
}
PRBool GetInFirstLine() const {
return GetFlag(LL_INFIRSTLINE);
}
void SetInFirstLine(PRBool aSetting) {
SetFlag(LL_INFIRSTLINE, aSetting);
}
//----------------------------------------
static PRBool TreatFrameAsBlock(nsIFrame* aFrame);
@ -390,8 +401,11 @@ public:
* some other kind of frame when inline frames are reflowed in a non-block
* context (e.g. MathML).
*/
nsIFrame* GetLineContainerFrame() { return mBlockReflowState->frame; }
nsIFrame* GetLineContainerFrame() const { return mBlockReflowState->frame; }
const nsLineList::iterator* GetLine() const {
return GetFlag(LL_GOTLINEBOX) ? &mLineBox : nsnull;
}
protected:
// This state is constant for a given block frame doing line layout
nsSpaceManager* mSpaceManager;
@ -427,7 +441,7 @@ protected:
PRInt32 mTextJustificationNumSpaces;
PRInt32 mTextJustificationNumLetters;
nsLineBox* mLineBox;
nsLineList::iterator mLineBox;
PRInt32 mTotalPlacedFrames;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,391 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* robert@ocallahan.org
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsTextFrameUtils.h"
#include "nsContentUtils.h"
#include "nsIWordBreaker.h"
#include "gfxFont.h"
#include "nsTextTransformer.h"
#include "nsCompressedCharMap.h"
#include "nsUnicharUtils.h"
// XXX TODO implement transform of backslash to yen that nsTextTransform does
// when requested by PresContext->LanguageSpecificTransformType(). Do it with
// a new factory type that just munges the input stream. But first, check
// that we really still need this, it's only enabled via a hidden pref
// which defaults false...
// Replaced by precompiled CCMap (see bug 180266). To update the list
// of characters, see one of files included below. As for the way
// the original list of characters was obtained by Frank Tang, see bug 54467.
// Updated to fix the regression (bug 263411). The list contains
// characters of the following Unicode character classes : Ps, Pi, Po, Pf, Pe.
// (ref.: http://www.w3.org/TR/2004/CR-CSS21-20040225/selector.html#first-letter)
// Note that the file does NOT yet include non-BMP characters because
// there's no point including them without fixing the way we identify
// 'first-letter' currently working only with BMP characters.
#include "punct_marks.ccmap"
DEFINE_CCMAP(gPuncCharsCCMap, const);
PRBool
nsTextFrameUtils::IsPunctuationMark(PRUnichar aChar)
{
return CCMAP_HAS_CHAR(gPuncCharsCCMap, aChar);
}
static PRBool IsDiscardable(PRUnichar ch, PRUint32* aFlags)
{
// Unlike IS_DISCARDABLE, we don't discard \r. \r will be ignored by gfxTextRun
// and discarding it would force us to copy text in many cases of preformatted
// text containing \r\n.
if (ch == CH_SHY) {
*aFlags |= nsTextFrameUtils::TEXT_HAS_SHY;
return PR_TRUE;
}
if ((ch & 0xFF00) != 0x2000) {
// Not a Bidi control character
return PR_FALSE;
}
return IS_BIDI_CONTROL(ch);
}
static PRBool IsDiscardable(PRUint8 ch, PRUint32* aFlags)
{
if (ch == CH_SHY) {
*aFlags |= nsTextFrameUtils::TEXT_HAS_SHY;
return PR_TRUE;
}
return PR_FALSE;
}
PRUnichar*
nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
PRUnichar* aOutput,
PRBool aCompressWhitespace,
PRPackedBool* aIncomingWhitespace,
gfxSkipCharsBuilder* aSkipChars,
PRUint32* aAnalysisFlags)
{
// We're just going to assume this!
PRUint32 flags = TEXT_HAS_NON_ASCII;
PRUnichar* outputStart = aOutput;
if (!aCompressWhitespace) {
// Convert tabs to spaces and skip discardables.
PRUint32 i;
for (i = 0; i < aLength; ++i) {
PRUnichar ch = *aText++;
if (ch == '\t') {
flags |= TEXT_HAS_TAB|TEXT_WAS_TRANSFORMED;
aSkipChars->KeepChar();
*aOutput++ = ' ';
} else if (IsDiscardable(ch, &flags)) {
aSkipChars->SkipChar();
} else {
aSkipChars->KeepChar();
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
} else if (IS_SURROGATE(ch)) {
flags |= gfxTextRunFactory::TEXT_HAS_SURROGATES;
}
*aOutput++ = ch;
}
}
} else {
PRBool inWhitespace = *aIncomingWhitespace;
PRUint32 i;
for (i = 0; i < aLength; ++i) {
PRUnichar ch = *aText++;
PRBool nowInWhitespace;
if (ch == ' ' &&
(i + 1 >= aLength ||
!IsSpaceCombiningSequenceTail(&aText[1], aLength - (i + 1)))) {
nowInWhitespace = PR_TRUE;
} else if (ch == '\n') {
if (i > 0 && IS_CJ_CHAR(aText[-1]) &&
i + 1 < aLength && IS_CJ_CHAR(aText[1])) {
// Discard newlines between CJK chars.
// XXX this really requires more context to get right!
aSkipChars->SkipChar();
continue;
}
nowInWhitespace = PR_TRUE;
} else {
nowInWhitespace = ch == '\t';
}
if (!nowInWhitespace) {
if (IsDiscardable(ch, &flags)) {
aSkipChars->SkipChar();
nowInWhitespace = inWhitespace;
} else {
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
} else if (IS_SURROGATE(ch)) {
flags |= gfxTextRunFactory::TEXT_HAS_SURROGATES;
}
*aOutput++ = ch;
aSkipChars->KeepChar();
}
} else {
if (inWhitespace) {
aSkipChars->SkipChar();
} else {
if (ch != ' ') {
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ' ';
aSkipChars->KeepChar();
}
}
inWhitespace = nowInWhitespace;
}
}
if (outputStart < aOutput) {
*aIncomingWhitespace = aOutput[-1] == ' ';
}
if (outputStart + aLength != aOutput) {
flags |= TEXT_WAS_TRANSFORMED;
}
*aAnalysisFlags = flags;
return aOutput;
}
PRUint8*
nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
PRUint8* aOutput,
PRBool aCompressWhitespace,
PRPackedBool* aIncomingWhitespace,
gfxSkipCharsBuilder* aSkipChars,
PRUint32* aAnalysisFlags)
{
PRUint32 flags = 0;
PRUint8 allBits = 0;
PRUint8* outputStart = aOutput;
if (!aCompressWhitespace) {
// Convert tabs to spaces and skip discardables.
PRUint32 i;
for (i = 0; i < aLength; ++i) {
PRUint8 ch = *aText++;
allBits |= ch;
if (ch == '\t') {
flags |= TEXT_HAS_TAB|TEXT_WAS_TRANSFORMED;
aSkipChars->KeepChar();
*aOutput++ = ' ';
} else if (IsDiscardable(ch, &flags)) {
aSkipChars->SkipChar();
} else {
aSkipChars->KeepChar();
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ch;
}
}
} else {
PRBool inWhitespace = *aIncomingWhitespace;
PRUint32 i;
for (i = 0; i < aLength; ++i) {
PRUint8 ch = *aText++;
allBits |= ch;
PRBool nowInWhitespace = ch == ' ' || ch == '\t' || ch == '\n';
if (!nowInWhitespace) {
if (IsDiscardable(ch, &flags)) {
aSkipChars->SkipChar();
nowInWhitespace = inWhitespace;
} else {
if (ch == CH_NBSP) {
ch = ' ';
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ch;
aSkipChars->KeepChar();
}
} else {
if (inWhitespace) {
aSkipChars->SkipChar();
} else {
if (ch != ' ') {
flags |= TEXT_WAS_TRANSFORMED;
}
*aOutput++ = ' ';
aSkipChars->KeepChar();
}
}
inWhitespace = nowInWhitespace;
}
}
if (outputStart < aOutput) {
*aIncomingWhitespace = aOutput[-1] == ' ';
}
if (outputStart + aLength != aOutput) {
flags |= TEXT_WAS_TRANSFORMED;
}
if (allBits & 0x80) {
flags |= TEXT_HAS_NON_ASCII;
}
*aAnalysisFlags = flags;
return aOutput;
}
// TODO The wordbreaker needs to be fixed. It's buggy, for example, it doesn't
// handle diacriticals combined with spaces
enum SimpleCharClass {
CLASS_ALNUM,
CLASS_PUNCT,
CLASS_SPACE
};
// This is what nsSampleWordBreaker::GetClass considers whitespace
static PRBool IsWordBreakerWhitespace(const PRUnichar* aChars, PRInt32 aLength)
{
PRUnichar ch = aChars[0];
if (ch == '\t' || ch == '\n' || ch == '\r')
return PR_TRUE;
if (ch == ' ' &&
!nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1))
return PR_TRUE;
return PR_FALSE;
}
// like nsSampleWordBreaker::GetClass
static SimpleCharClass Classify8BitChar(PRUint8 aChar)
{
if (aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r')
return CLASS_SPACE;
if ((aChar >= 'a' && aChar <= 'z') || (aChar >= 'A' || aChar <= 'Z') ||
(aChar >= '0' && aChar <= '9') || (aChar >= 128))
return CLASS_ALNUM;
return CLASS_PUNCT;
}
PRInt32
nsTextFrameUtils::FindWordBoundary(const nsTextFragment* aText,
gfxTextRun* aTextRun,
gfxSkipCharsIterator* aIterator,
PRInt32 aOffset, PRInt32 aLength,
PRInt32 aPosition, PRInt32 aDirection,
PRBool aBreakBeforePunctuation,
PRBool aBreakAfterPunctuation,
PRBool* aWordIsWhitespace)
{
// A space followed by combining diacritical marks is not whitespace!!
PRInt32 textLength = aText->GetLength();
*aWordIsWhitespace = aText->Is2b()
? IsWordBreakerWhitespace(aText->Get2b() + aPosition, textLength - aPosition)
: Classify8BitChar(aText->Get1b()[aPosition]) == CLASS_SPACE;
PRInt32 nextWordPos; // first character of next/prev "word"
if (aText->Is2b()) {
nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
const PRUnichar* text = aText->Get2b();
PRInt32 pos = aPosition;
// XXX the wordbreaker currently isn't cluster-aware. We need to make
// it cluster-aware. In the meantime, just reject any word breaks
// inside clusters.
for (;;) {
nextWordPos = aDirection > 0
? wordBreaker->NextWord(text, textLength, pos)
: wordBreaker->PrevWord(text, textLength, pos);
if (nextWordPos < 0)
break;
if (aTextRun->GetCharFlags(aIterator->ConvertOriginalToSkipped(nextWordPos)) & gfxTextRun::CLUSTER_START)
break;
pos = nextWordPos;
}
} else {
const char* text = aText->Get1b();
SimpleCharClass cl = Classify8BitChar(text[aPosition]);
nextWordPos = aPosition;
// There shouldn't be any clusters in 8bit text but we'll cover that
// possibility anyway
do {
nextWordPos += aDirection;
if (nextWordPos < aOffset || nextWordPos >= aOffset + aLength) {
nextWordPos = NS_WORDBREAKER_NEED_MORE_TEXT;
break;
}
} while (Classify8BitChar(text[nextWordPos]) == cl ||
!(aTextRun->GetCharFlags(aIterator->ConvertOriginalToSkipped(nextWordPos)) & gfxTextRun::CLUSTER_START));
}
// Handle punctuation breaks
PRInt32 i;
PRBool punctPrev = IsPunctuationMark(aText->CharAt(aPosition));
for (i = aPosition + aDirection;
i != nextWordPos && i >= aOffset && i < aOffset + aLength;
i += aDirection) {
// See if there's a punctuation break between i-aDirection and i
PRBool punct = IsPunctuationMark(aText->CharAt(i));
if (punct != punctPrev &&
(aTextRun->GetCharFlags(aIterator->ConvertOriginalToSkipped(i)) & gfxTextRun::CLUSTER_START)) {
PRBool punctIsBefore = aDirection < 0 ? punct : punctPrev;
if (punctIsBefore ? aBreakAfterPunctuation : aBreakBeforePunctuation)
break;
}
punctPrev = punct;
}
if (i < aOffset || i >= aOffset + aLength)
return -1;
return i;
}
PRBool nsSkipCharsRunIterator::NextRun() {
do {
if (!mRemainingLength)
return PR_FALSE;
if (mRunLength) {
mIterator.AdvanceOriginal(mRunLength);
NS_ASSERTION(mRunLength > 0, "No characters in run (initial length too large?)");
if (!mSkipped || mLengthIncludesSkipped) {
mRemainingLength -= mRunLength;
}
}
PRInt32 length;
mSkipped = mIterator.IsOriginalCharSkipped(&length);
mRunLength = PR_MIN(length, mRemainingLength);
} while (!mVisitSkipped && mSkipped);
return PR_TRUE;
}

View File

@ -0,0 +1,179 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* robert@ocallahan.org
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NSTEXTFRAMEUTILS_H_
#define NSTEXTFRAMEUTILS_H_
#include "gfxFont.h"
#include "gfxSkipChars.h"
#include "nsTextFragment.h"
#define BIG_TEXT_NODE_SIZE 4096
class nsTextFrameUtils {
public:
// These constants are used as textrun flags for textframe textruns.
enum {
// The following flags are set by TransformText
// the text has at least one untransformed tab character
TEXT_HAS_TAB = 0x010000,
// the original text has at least one soft hyphen character
TEXT_HAS_SHY = 0x020000,
TEXT_HAS_NON_ASCII = 0x040000,
TEXT_WAS_TRANSFORMED = 0x080000,
// The following flags are set by nsTextFrame
TEXT_IS_SIMPLE_FLOW = 0x100000,
TEXT_INCOMING_WHITESPACE = 0x200000
};
static PRBool
IsPunctuationMark(PRUnichar aChar);
/**
* Returns PR_TRUE if aChars/aLength are something that make a space
* character not be whitespace when they follow the space character.
* For now, this is true if and only if aChars starts with a ZWJ. (This
* is what Uniscribe assumes.)
*/
static PRBool
IsSpaceCombiningSequenceTail(const PRUnichar* aChars, PRInt32 aLength) {
return aLength > 0 && aChars[0] == 0x200D; // ZWJ
}
/**
* Create a text run from a run of Unicode text. The text may have whitespace
* compressed. A preformatted tab is sent to the text run as a single space.
* (Tab spacing must be performed by textframe later.) Certain other
* characters are discarded.
*
* @param aCompressWhitespace runs of consecutive whitespace (spaces not
* followed by a diacritical mark, tabs, and newlines) are compressed to a
* single space character.
*/
static PRUnichar* TransformText(const PRUnichar* aText, PRUint32 aLength,
PRUnichar* aOutput,
PRBool aCompressWhitespace,
PRPackedBool* aIncomingWhitespace,
gfxSkipCharsBuilder* aSkipChars,
PRUint32* aAnalysisFlags);
static PRUint8* TransformText(const PRUint8* aText, PRUint32 aLength,
PRUint8* aOutput,
PRBool aCompressWhitespace,
PRPackedBool* aIncomingWhitespace,
gfxSkipCharsBuilder* aSkipChars,
PRUint32* aAnalysisFlags);
/**
* Find a word boundary starting from a given position and proceeding either
* forwards (aDirection == 1) or backwards (aDirection == -1). The search
* is limited to a substring of an nsTextFragment. We return the index
* of the character that is the first character of the next/prev word; the
* result can be aOffset <= result <= aLength (result == aLength means
* that there's definitely a word boundary at the end of the text), or -1 to
* indicate that no boundary was found.
*
* @param aTextRun a text run which we will use to ensure that we don't
* return a boundary inside a cluster
* @param aPosition a character in the substring aOffset/aLength
* @param aBreakBeforePunctuation if true, then we allow a word break
* when transitioning from regular word text to punctuation (in content order)
* @param aBreakAfterPunctuation if true, then we allow a word break
* when transitioning from punctuation to regular word text (in content order)
* @param aWordIsWhitespace we set this to true if the word-part we skipped
* over is whitespace
*
* For the above properties, "punctuation" is defined as any ASCII character
* which is not a letter or a digit. Regular word text is any non-whitespace
* (here "whitespace" includes non-breaking whitespace).
* Word break points are the punctuation breaks defined above, plus
* for Unicode text, whatever intl's wordbreaker identifies, and for
* ASCII text, boundaries between whitespace and non-whitespace.
*/
static PRInt32
FindWordBoundary(const nsTextFragment* aText,
gfxTextRun* aTextRun,
gfxSkipCharsIterator* aIterator,
PRInt32 aOffset, PRInt32 aLength,
PRInt32 aPosition, PRInt32 aDirection,
PRBool aBreakBeforePunctuation,
PRBool aBreakAfterPunctuation,
PRBool* aWordIsWhitespace);
};
class nsSkipCharsRunIterator {
public:
enum LengthMode {
LENGTH_UNSKIPPED_ONLY = PR_FALSE,
LENGTH_INCLUDES_SKIPPED = PR_TRUE
};
nsSkipCharsRunIterator(const gfxSkipCharsIterator& aStart,
LengthMode aLengthIncludesSkipped, PRUint32 aLength)
: mIterator(aStart), mRemainingLength(aLength), mRunLength(0),
mVisitSkipped(PR_FALSE),
mLengthIncludesSkipped(aLengthIncludesSkipped) {
}
void SetVisitSkipped() { mVisitSkipped = PR_TRUE; }
void SetOriginalOffset(PRInt32 aOffset) {
mIterator.SetOriginalOffset(aOffset);
}
void SetSkippedOffset(PRUint32 aOffset) {
mIterator.SetSkippedOffset(aOffset);
}
// guaranteed to return only positive-length runs
PRBool NextRun();
PRBool IsSkipped() const { return mSkipped; }
// Always returns something > 0
PRInt32 GetRunLength() const { return mRunLength; }
const gfxSkipCharsIterator& GetPos() const { return mIterator; }
PRInt32 GetOriginalOffset() const { return mIterator.GetOriginalOffset(); }
PRUint32 GetSkippedOffset() const { return mIterator.GetSkippedOffset(); }
private:
gfxSkipCharsIterator mIterator;
PRInt32 mRemainingLength;
PRInt32 mRunLength;
PRPackedBool mSkipped;
PRPackedBool mVisitSkipped;
PRPackedBool mLengthIncludesSkipped;
};
#endif /*NSTEXTFRAMEUTILS_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Novell code.
*
* The Initial Developer of the Original Code is Novell Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* robert@ocallahan.org
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NSTEXTRUNTRANSFORMATIONS_H_
#define NSTEXTRUNTRANSFORMATIONS_H_
#include "gfxFont.h"
class nsCaseTransformingTextRun;
class nsStyleContext;
class nsTransformingTextRunFactory : public gfxTextRunFactory {
public:
nsTransformingTextRunFactory(gfxTextRunFactory* aInnerTextRunFactory,
nsTransformingTextRunFactory* aInnerTransformingTextRunFactory)
: mInnerTextRunFactory(aInnerTextRunFactory),
mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory),
mStyles(nsnull) {}
// Assign style contexts to each character in the string. Call this before
// calling MakeTextRun. The array needs to have a lifetime covering the
// call to MakeTextRun.
virtual void SetStyles(nsStyleContext** aStyles) { mStyles = aStyles; }
// Default 8-bit path just transforms to Unicode and takes that path
virtual gfxTextRun* MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams);
// Redeclare this to make C++ compiler shut up
virtual gfxTextRun* MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams) = 0;
protected:
nsRefPtr<gfxTextRunFactory> mInnerTextRunFactory;
nsRefPtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
nsStyleContext** mStyles;
};
/**
* Builds textruns that render their text using a font-variant (i.e.,
* smallcaps).
*/
class nsFontVariantTextRunFactory : public nsTransformingTextRunFactory {
public:
nsFontVariantTextRunFactory(gfxFontGroup* aFontGroup)
: nsTransformingTextRunFactory(aFontGroup, nsnull), mFontGroup(aFontGroup) {}
// Redeclare this to make C++ compiler shut up
virtual gfxTextRun* MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams) {
return nsTransformingTextRunFactory::MakeTextRun(aString, aLength, aParams);
}
virtual gfxTextRun* MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams);
private:
nsRefPtr<gfxFontGroup> mFontGroup;
};
/**
* Builds textruns that transform the text in some way (e.g., capitalize)
* and then render the text using some other textrun implementation.
*/
class nsCaseTransformTextRunFactory : public nsTransformingTextRunFactory {
public:
nsCaseTransformTextRunFactory(gfxTextRunFactory* aInnerTextRunFactory,
nsTransformingTextRunFactory* aInnerTransformingTextRunFactory,
PRBool aAllUppercase = PR_FALSE)
: nsTransformingTextRunFactory(aInnerTextRunFactory, aInnerTransformingTextRunFactory),
mAllUppercase(aAllUppercase) {}
// Redeclare this to make C++ compiler shut up
virtual gfxTextRun* MakeTextRun(const PRUint8* aString, PRUint32 aLength,
Parameters* aParams) {
return nsTransformingTextRunFactory::MakeTextRun(aString, aLength, aParams);
}
virtual gfxTextRun* MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
Parameters* aParams);
// We need these for the implementation of case-transforming text runs
gfxTextRunFactory* GetInnerTextRunFactory() { return mInnerTextRunFactory; }
nsTransformingTextRunFactory* GetInnerTransformingTextRunFactory() {
return mInnerTransformingTextRunFactory;
}
nsStyleContext** GetStyles() { return mStyles; }
PRBool IsAllUppercase() { return mAllUppercase; }
private:
PRPackedBool mAllUppercase;
};
#endif /*NSTEXTRUNTRANSFORMATIONS_H_*/

View File

@ -59,7 +59,7 @@
#include "nsLayoutAtoms.h"
#endif
nsICaseConversion* nsTextTransformer::gCaseConv = nsnull;
PRBool nsTextTransformer::sWordSelectListenerPrefChecked = PR_FALSE;
PRBool nsTextTransformer::sWordSelectEatSpaceAfter = PR_FALSE;
PRBool nsTextTransformer::sWordSelectStopAtPunctuation = PR_FALSE;
@ -120,8 +120,6 @@ nsAutoTextBuffer::GrowTo(PRInt32 aNewSize, PRBool aCopyToHead)
//----------------------------------------------------------------------
static nsICaseConversion* gCaseConv = nsnull;
nsresult
nsTextTransformer::Initialize()
{
@ -142,7 +140,9 @@ nsTextTransformer::Initialize()
return NS_OK;
}
static nsresult EnsureCaseConv()
nsresult
nsTextTransformer::EnsureCaseConv()
{
nsresult res = NS_OK;
if (!gCaseConv) {
@ -153,6 +153,13 @@ static nsresult EnsureCaseConv()
return res;
}
nsICaseConversion*
nsTextTransformer::GetCaseConv()
{
EnsureCaseConv();
return gCaseConv;
}
void
nsTextTransformer::Shutdown()
{

View File

@ -56,6 +56,7 @@ class nsIContent;
class nsIFrame;
class nsILineBreaker;
class nsIWordBreaker;
class nsICaseConversion;
// XXX I'm sure there are other special characters
#define CH_NBSP 160
@ -345,6 +346,8 @@ public:
return sWordSelectStopAtPunctuation;
}
static nsICaseConversion* GetCaseConv();
static nsresult Initialize();
static void Shutdown();
@ -378,6 +381,8 @@ protected:
PRInt32 ScanPreWrapWhiteSpace_B(PRInt32* aWordLen);
PRInt32 ScanPreData_B(PRInt32* aWordLen);
static nsresult EnsureCaseConv();
// Converts the current text in the transform buffer from ascii to
// Unicode
void ConvertTransformedTextToUnicode();
@ -429,6 +434,8 @@ protected:
static PRBool sWordSelectEatSpaceAfter; // should we include whitespace up to next word?
static PRBool sWordSelectStopAtPunctuation; // should we stop at punctuation?
static nsICaseConversion* gCaseConv;
#ifdef DEBUG
static void SelfTest(nsPresContext* aPresContext);

View File

@ -985,7 +985,7 @@ nsMathMLContainerFrame::ReflowForeignChild(nsIFrame* aChildFrame,
// provide a local, self-contained linelayout where to reflow the nsInlineFrame
nsSize availSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsLineLayout ll(aPresContext, aReflowState.mSpaceManager,
aReflowState.parentReflowState);
aReflowState.parentReflowState, nsnull);
ll.BeginLineReflow(0, 0, availSize.width, availSize.height, PR_FALSE, PR_FALSE);
PRBool pushedFrame;
ll.ReflowFrame(aChildFrame, aStatus, &aDesiredSize, pushedFrame);