mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-09 00:11:44 +00:00
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:
parent
a8c339859b
commit
e6bad38abe
@ -72,6 +72,7 @@ nsCaseTreatment.h \
|
||||
nsContentCID.h \
|
||||
nsCopySupport.h \
|
||||
nsContentCreatorFunctions.h \
|
||||
nsLineBreaker.h \
|
||||
nsXMLNameSpaceMap.h \
|
||||
$(NULL)
|
||||
|
||||
|
169
content/base/public/nsLineBreaker.h
Normal file
169
content/base/public/nsLineBreaker.h
Normal 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_*/
|
@ -124,6 +124,7 @@ CPPSRCS = \
|
||||
nsGkAtoms.cpp \
|
||||
nsHTMLContentSerializer.cpp \
|
||||
nsImageLoadingContent.cpp \
|
||||
nsLineBreaker.cpp \
|
||||
nsLoadListenerProxy.cpp \
|
||||
nsMappedAttributes.cpp \
|
||||
nsNameSpaceManager.cpp \
|
||||
|
313
content/base/src/nsLineBreaker.cpp
Normal file
313
content/base/src/nsLineBreaker.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -124,7 +124,7 @@ CPPSRCS += nsSystemFontsMac.cpp
|
||||
endif
|
||||
endif
|
||||
|
||||
EXPORTS += nsIThebesRenderingContext.h
|
||||
EXPORTS += nsIThebesRenderingContext.h nsIThebesFontMetrics.h
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/. \
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -22,6 +22,7 @@ EXPORTS = gfxASurface.h \
|
||||
gfxPoint.h \
|
||||
gfxRect.h \
|
||||
gfxRegion.h \
|
||||
gfxSkipChars.h \
|
||||
gfxTypes.h \
|
||||
gfxTextRunCache.h \
|
||||
$(NULL)
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
**/
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
};
|
||||
|
||||
|
333
gfx/thebes/public/gfxSkipChars.h
Normal file
333
gfx/thebes/public/gfxSkipChars.h
Normal 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*/
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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*
|
||||
|
@ -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
69
gfx/thebes/src/gfxRect.cpp
Normal file
69
gfx/thebes/src/gfxRect.cpp
Normal 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;
|
||||
}
|
243
gfx/thebes/src/gfxSkipChars.cpp
Normal file
243
gfx/thebes/src/gfxSkipChars.cpp
Normal 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;
|
||||
}
|
@ -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, ¶ms);
|
||||
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, ¶ms);
|
||||
}
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
}
|
||||
if (tr) {
|
||||
// We don't want to have to reconstruct the string
|
||||
tr->RememberText(str, aLength);
|
||||
}
|
||||
|
||||
return tr;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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()) {
|
||||
|
@ -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 = ≪
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
5210
layout/generic/nsTextFrameThebes.cpp
Normal file
5210
layout/generic/nsTextFrameThebes.cpp
Normal file
File diff suppressed because it is too large
Load Diff
391
layout/generic/nsTextFrameUtils.cpp
Normal file
391
layout/generic/nsTextFrameUtils.cpp
Normal 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;
|
||||
}
|
179
layout/generic/nsTextFrameUtils.h
Normal file
179
layout/generic/nsTextFrameUtils.h
Normal 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_*/
|
1185
layout/generic/nsTextRunTransformations.cpp
Normal file
1185
layout/generic/nsTextRunTransformations.cpp
Normal file
File diff suppressed because it is too large
Load Diff
124
layout/generic/nsTextRunTransformations.h
Normal file
124
layout/generic/nsTextRunTransformations.h
Normal 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_*/
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user