mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
1256 lines
41 KiB
C++
1256 lines
41 KiB
C++
|
/* -*- 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 Communicator client 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):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either of 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 ***** */
|
||
|
|
||
|
#ifdef IBMBIDI
|
||
|
|
||
|
#include "nsBidiPresUtils.h"
|
||
|
#include "nsITextContent.h"
|
||
|
#include "nsTextFragment.h"
|
||
|
#include "nsLayoutAtoms.h"
|
||
|
#include "nsPresContext.h"
|
||
|
#include "nsIRenderingContext.h"
|
||
|
#include "nsIServiceManager.h"
|
||
|
#include "nsFrameManager.h"
|
||
|
#include "nsBidiFrames.h"
|
||
|
#include "nsBidiUtils.h"
|
||
|
|
||
|
static const PRUnichar kSpace = 0x0020;
|
||
|
static const PRUnichar kLineSeparator = 0x2028;
|
||
|
static const PRUnichar kObjectSubstitute = 0xFFFC;
|
||
|
static const PRUnichar kLRE = 0x202A;
|
||
|
static const PRUnichar kRLE = 0x202B;
|
||
|
static const PRUnichar kLRO = 0x202D;
|
||
|
static const PRUnichar kRLO = 0x202E;
|
||
|
static const PRUnichar kPDF = 0x202C;
|
||
|
static const PRUnichar ALEF = 0x05D0;
|
||
|
|
||
|
#define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
|
||
|
// Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
|
||
|
|
||
|
nsresult
|
||
|
NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsIFrame** aResult);
|
||
|
nsresult
|
||
|
NS_NewDirectionalFrame(nsIFrame** aNewFrame, PRUnichar aChar);
|
||
|
|
||
|
nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
|
||
|
mIndexMap(nsnull),
|
||
|
mLevels(nsnull),
|
||
|
mSuccess(NS_ERROR_FAILURE),
|
||
|
mBidiEngine(nsnull)
|
||
|
{
|
||
|
mBidiEngine = new nsBidi();
|
||
|
if (mBidiEngine && mContentToFrameIndex.Init()) {
|
||
|
mSuccess = NS_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsBidiPresUtils::~nsBidiPresUtils()
|
||
|
{
|
||
|
if (mLevels) {
|
||
|
delete[] mLevels;
|
||
|
}
|
||
|
if (mIndexMap) {
|
||
|
delete[] mIndexMap;
|
||
|
}
|
||
|
delete mBidiEngine;
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsBidiPresUtils::IsSuccessful() const
|
||
|
{
|
||
|
return NS_SUCCEEDED(mSuccess);
|
||
|
}
|
||
|
|
||
|
/* Some helper methods for Resolve() */
|
||
|
|
||
|
static nsresult
|
||
|
CreateBidiContinuation(nsPresContext* aPresContext,
|
||
|
nsIContent* aContent,
|
||
|
nsIFrame* aFrame,
|
||
|
nsIFrame** aNewFrame)
|
||
|
{
|
||
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
||
|
|
||
|
*aNewFrame = nsnull;
|
||
|
|
||
|
NS_PRECONDITION(aFrame, "null ptr");
|
||
|
|
||
|
nsIPresShell *presShell = aPresContext->PresShell();
|
||
|
|
||
|
NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
|
||
|
|
||
|
NS_NewContinuingTextFrame(presShell, aNewFrame);
|
||
|
if (!(*aNewFrame) ) {
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
nsStyleContext* styleContext = aFrame->GetStyleContext();
|
||
|
|
||
|
NS_ASSERTION(styleContext, "Frame has no styleContext in nsBidiPresUtils::CreateBidiContinuation");
|
||
|
|
||
|
nsIFrame* parent = aFrame->GetParent();
|
||
|
NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
|
||
|
|
||
|
(*aNewFrame)->Init(aPresContext, aContent, parent, styleContext, nsnull);
|
||
|
|
||
|
// XXX: TODO: Instead, create and insert entire frame list
|
||
|
(*aNewFrame)->SetNextSibling(nsnull);
|
||
|
|
||
|
// The list name nsLayoutAtoms::nextBidi would indicate we don't want reflow
|
||
|
parent->InsertFrames(nsLayoutAtoms::nextBidi, aFrame, *aNewFrame);
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
/*
|
||
|
* Overview of the implementation of Resolve():
|
||
|
*
|
||
|
* Walk through the descendants of aBlockFrame and build:
|
||
|
* * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
|
||
|
* * mBuffer: an nsAutoString containing a representation of
|
||
|
* the content of the frames.
|
||
|
* In the case of text frames, this is the actual text context of the
|
||
|
* frames, but some other elements are represented in a symbolic form which
|
||
|
* will make the Unicode Bidi Algorithm give the correct results.
|
||
|
* Bidi embeddings and overrides set by CSS or <bdo> elements are
|
||
|
* represented by the corresponding Unicode control characters.
|
||
|
* <br> elements are represented by U+2028 LINE SEPARATOR
|
||
|
* Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
|
||
|
* CHARACTER
|
||
|
*
|
||
|
* Then pass mBuffer to the Bidi engine for resolving of embedding levels
|
||
|
* by nsBidi::SetPara() and division into directional runs by
|
||
|
* nsBidi::CountRuns().
|
||
|
*
|
||
|
* Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
|
||
|
* correlate them with the frames indexed in mLogicalArray, setting the
|
||
|
* baseLevel, embeddingLevel, and charType properties according to the results
|
||
|
* returned by the Bidi engine and CalculateCharType().
|
||
|
*
|
||
|
* The rendering layer requires each text frame to contain text in only one
|
||
|
* direction and of only one character type, so we may need to call
|
||
|
* EnsureBidiContinuation() to split frames. We may also need to call
|
||
|
* RemoveBidiContinuation() to delete frames created by
|
||
|
* EnsureBidiContinuation() in previous reflows.
|
||
|
*/
|
||
|
nsresult
|
||
|
nsBidiPresUtils::Resolve(nsPresContext* aPresContext,
|
||
|
nsIFrame* aBlockFrame,
|
||
|
nsIFrame* aFirstChild,
|
||
|
PRBool& aForceReflow,
|
||
|
PRBool aIsVisualFormControl)
|
||
|
{
|
||
|
aForceReflow = PR_FALSE;
|
||
|
mLogicalFrames.Clear();
|
||
|
mContentToFrameIndex.Clear();
|
||
|
|
||
|
// handle bidi-override being set on the block itself before calling
|
||
|
// InitLogicalArray.
|
||
|
const nsStyleVisibility* vis = aBlockFrame->GetStyleVisibility();
|
||
|
const nsStyleTextReset* text = aBlockFrame->GetStyleTextReset();
|
||
|
|
||
|
if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
|
||
|
nsresult rv = NS_OK;
|
||
|
nsIFrame *directionalFrame = nsnull;
|
||
|
if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kRLO);
|
||
|
}
|
||
|
else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kLRO);
|
||
|
}
|
||
|
if (directionalFrame && NS_SUCCEEDED(rv)) {
|
||
|
mLogicalFrames.AppendElement(directionalFrame);
|
||
|
}
|
||
|
}
|
||
|
mSuccess = InitLogicalArray(aPresContext, aFirstChild, nsnull, PR_TRUE);
|
||
|
if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
|
||
|
nsIFrame *directionalFrame = nsnull;
|
||
|
nsresult rv = NS_NewDirectionalFrame(&directionalFrame, kPDF);
|
||
|
if (directionalFrame && NS_SUCCEEDED(rv)) {
|
||
|
mLogicalFrames.AppendElement(directionalFrame);
|
||
|
}
|
||
|
}
|
||
|
if (NS_FAILED(mSuccess) ) {
|
||
|
return mSuccess;
|
||
|
}
|
||
|
|
||
|
CreateBlockBuffer(aPresContext);
|
||
|
|
||
|
PRInt32 bufferLength = mBuffer.Length();
|
||
|
|
||
|
if (bufferLength < 1) {
|
||
|
mSuccess = NS_OK;
|
||
|
return mSuccess;
|
||
|
}
|
||
|
PRInt32 runCount;
|
||
|
PRUint8 embeddingLevel;
|
||
|
|
||
|
nsBidiLevel paraLevel = embeddingLevel =
|
||
|
(NS_STYLE_DIRECTION_RTL == vis->mDirection)
|
||
|
? NSBIDI_RTL : NSBIDI_LTR;
|
||
|
|
||
|
mSuccess = mBidiEngine->SetPara(mBuffer.get(), bufferLength, paraLevel, nsnull);
|
||
|
if (NS_FAILED(mSuccess) ) {
|
||
|
return mSuccess;
|
||
|
}
|
||
|
|
||
|
PRBool isVisual;
|
||
|
if (aIsVisualFormControl) {
|
||
|
isVisual = PR_FALSE;
|
||
|
} else {
|
||
|
isVisual = aPresContext->IsVisualMode();
|
||
|
}
|
||
|
mSuccess = mBidiEngine->CountRuns(&runCount);
|
||
|
if (NS_FAILED(mSuccess) ) {
|
||
|
return mSuccess;
|
||
|
}
|
||
|
PRInt32 runLength = 0;
|
||
|
PRInt32 fragmentLength = 0;
|
||
|
PRInt32 temp;
|
||
|
PRInt32 frameIndex = -1;
|
||
|
PRInt32 frameCount = mLogicalFrames.Count();
|
||
|
PRInt32 contentOffset = 0; // offset within current frame
|
||
|
PRInt32 lineOffset = 0; // offset within mBuffer
|
||
|
PRInt32 logicalLimit = 0;
|
||
|
PRInt32 numRun = -1;
|
||
|
PRUint8 charType;
|
||
|
PRUint8 prevType = eCharType_LeftToRight;
|
||
|
PRBool isTextFrame = PR_FALSE;
|
||
|
nsIFrame* frame = nsnull;
|
||
|
nsIFrame* nextBidi;
|
||
|
nsIContent* content = nsnull;
|
||
|
nsCOMPtr<nsITextContent> textContent;
|
||
|
const nsTextFragment* fragment;
|
||
|
nsIAtom* frameType = nsnull;
|
||
|
|
||
|
nsPropertyTable *propTable = aPresContext->PropertyTable();
|
||
|
|
||
|
for (; ;) {
|
||
|
if (fragmentLength <= 0) {
|
||
|
if (++frameIndex >= frameCount) {
|
||
|
break;
|
||
|
}
|
||
|
contentOffset = 0;
|
||
|
|
||
|
frame = (nsIFrame*) (mLogicalFrames[frameIndex]);
|
||
|
frameType = frame->GetType();
|
||
|
if (nsLayoutAtoms::textFrame == frameType) {
|
||
|
content = frame->GetContent();
|
||
|
if (!content) {
|
||
|
mSuccess = NS_OK;
|
||
|
break;
|
||
|
}
|
||
|
textContent = do_QueryInterface(content, &mSuccess);
|
||
|
if (NS_FAILED(mSuccess) || (!textContent) ) {
|
||
|
break;
|
||
|
}
|
||
|
fragment = textContent->Text();
|
||
|
if (!fragment) {
|
||
|
mSuccess = NS_ERROR_FAILURE;
|
||
|
break;
|
||
|
}
|
||
|
fragmentLength = fragment->GetLength();
|
||
|
isTextFrame = PR_TRUE;
|
||
|
} // if text frame
|
||
|
else {
|
||
|
isTextFrame = PR_FALSE;
|
||
|
fragmentLength = 1;
|
||
|
}
|
||
|
} // if (fragmentLength <= 0)
|
||
|
if (runLength <= 0) {
|
||
|
if (++numRun >= runCount) {
|
||
|
break;
|
||
|
}
|
||
|
lineOffset = logicalLimit;
|
||
|
if (NS_FAILED(mBidiEngine->GetLogicalRun(
|
||
|
lineOffset, &logicalLimit, &embeddingLevel) ) ) {
|
||
|
break;
|
||
|
}
|
||
|
runLength = logicalLimit - lineOffset;
|
||
|
if (isVisual) {
|
||
|
embeddingLevel = paraLevel;
|
||
|
}
|
||
|
} // if (runLength <= 0)
|
||
|
|
||
|
if (nsLayoutAtoms::directionalFrame == frameType) {
|
||
|
delete frame;
|
||
|
++lineOffset;
|
||
|
}
|
||
|
else {
|
||
|
propTable->SetProperty(frame, nsLayoutAtoms::embeddingLevel,
|
||
|
NS_INT32_TO_PTR(embeddingLevel), nsnull, nsnull);
|
||
|
propTable->SetProperty(frame, nsLayoutAtoms::baseLevel,
|
||
|
NS_INT32_TO_PTR(paraLevel), nsnull, nsnull);
|
||
|
if (isTextFrame) {
|
||
|
PRInt32 typeLimit = PR_MIN(logicalLimit, lineOffset + fragmentLength);
|
||
|
CalculateCharType(lineOffset, typeLimit, logicalLimit, runLength,
|
||
|
runCount, charType, prevType);
|
||
|
// IBMBIDI - Egypt - Start
|
||
|
propTable->SetProperty(frame, nsLayoutAtoms::charType,
|
||
|
NS_INT32_TO_PTR(charType), nsnull, nsnull);
|
||
|
// IBMBIDI - Egypt - End
|
||
|
|
||
|
if ( (runLength > 0) && (runLength < fragmentLength) ) {
|
||
|
if (!EnsureBidiContinuation(aPresContext, content, frame,
|
||
|
&nextBidi, frameIndex) ) {
|
||
|
break;
|
||
|
}
|
||
|
frame->AdjustOffsetsForBidi(contentOffset, contentOffset + runLength);
|
||
|
frame = nextBidi;
|
||
|
contentOffset += runLength;
|
||
|
} // if (runLength < fragmentLength)
|
||
|
else {
|
||
|
frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
|
||
|
PRInt32 newIndex = 0;
|
||
|
mContentToFrameIndex.Get(content, &newIndex);
|
||
|
if (newIndex > frameIndex) {
|
||
|
RemoveBidiContinuation(aPresContext, frame,
|
||
|
frameIndex, newIndex, temp);
|
||
|
aForceReflow = PR_TRUE;
|
||
|
runLength -= temp;
|
||
|
fragmentLength -= temp;
|
||
|
lineOffset += temp;
|
||
|
frameIndex = newIndex;
|
||
|
}
|
||
|
}
|
||
|
} // isTextFrame
|
||
|
else {
|
||
|
++lineOffset;
|
||
|
}
|
||
|
} // not directionalFrame
|
||
|
temp = runLength;
|
||
|
runLength -= fragmentLength;
|
||
|
fragmentLength -= temp;
|
||
|
} // for
|
||
|
return mSuccess;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsBidiPresUtils::InitLogicalArray(nsPresContext* aPresContext,
|
||
|
nsIFrame* aCurrentFrame,
|
||
|
nsIFrame* aNextInFlow,
|
||
|
PRBool aAddMarkers)
|
||
|
{
|
||
|
nsIFrame* frame;
|
||
|
nsIFrame* directionalFrame;
|
||
|
nsresult rv;
|
||
|
nsresult res = NS_OK;
|
||
|
|
||
|
for (frame = aCurrentFrame;
|
||
|
frame && frame != aNextInFlow;
|
||
|
frame = frame->GetNextSibling()) {
|
||
|
rv = NS_ERROR_FAILURE;
|
||
|
const nsStyleDisplay* display = frame->GetStyleDisplay();
|
||
|
|
||
|
if (aAddMarkers && !display->IsBlockLevel() ) {
|
||
|
const nsStyleVisibility* vis = frame->GetStyleVisibility();
|
||
|
const nsStyleTextReset* text = frame->GetStyleTextReset();
|
||
|
switch (text->mUnicodeBidi) {
|
||
|
case NS_STYLE_UNICODE_BIDI_NORMAL:
|
||
|
break;
|
||
|
case NS_STYLE_UNICODE_BIDI_EMBED:
|
||
|
if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kRLE);
|
||
|
}
|
||
|
else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kLRE);
|
||
|
}
|
||
|
break;
|
||
|
case NS_STYLE_UNICODE_BIDI_OVERRIDE:
|
||
|
if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kRLO);
|
||
|
}
|
||
|
else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kLRO);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
mLogicalFrames.AppendElement(directionalFrame);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsIAtom* frameType = frame->GetType();
|
||
|
|
||
|
if ( (!display->IsBlockLevel() )
|
||
|
&& ( (nsLayoutAtoms::inlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::positionedInlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::letterFrame == frameType)
|
||
|
|| (nsLayoutAtoms::blockFrame == frameType) ) ) {
|
||
|
nsIFrame* kid = frame->GetFirstChild(nsnull);
|
||
|
res = InitLogicalArray(aPresContext, kid, aNextInFlow, aAddMarkers);
|
||
|
}
|
||
|
else {
|
||
|
/* Bidi leaf frame: add the frame to the mLogicalFrames array,
|
||
|
* and add its index to the mContentToFrameIndex hashtable. This
|
||
|
* will be used in RemoveBidiContinuation() to identify the last
|
||
|
* frame in the array with a given content.
|
||
|
*/
|
||
|
nsIContent* content = frame->GetContent();
|
||
|
if (content) {
|
||
|
mContentToFrameIndex.Put(content, mLogicalFrames.Count());
|
||
|
}
|
||
|
mLogicalFrames.AppendElement(frame);
|
||
|
}
|
||
|
|
||
|
// If the element is attributed by dir, indicate direction pop (add PDF frame)
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
rv = NS_NewDirectionalFrame(&directionalFrame, kPDF);
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
mLogicalFrames.AppendElement(directionalFrame);
|
||
|
}
|
||
|
}
|
||
|
} // for
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::CreateBlockBuffer(nsPresContext* aPresContext)
|
||
|
{
|
||
|
mBuffer.SetLength(0);
|
||
|
|
||
|
nsIFrame* frame;
|
||
|
nsIContent* prevContent = nsnull;
|
||
|
nsCOMPtr<nsITextContent> textContent;
|
||
|
PRUint32 i;
|
||
|
PRUint32 count = mLogicalFrames.Count();
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
frame = (nsIFrame*) (mLogicalFrames[i]);
|
||
|
nsIAtom* frameType = frame->GetType();
|
||
|
|
||
|
if (nsLayoutAtoms::textFrame == frameType) {
|
||
|
nsIContent* content = frame->GetContent();
|
||
|
if (!content) {
|
||
|
mSuccess = NS_OK;
|
||
|
break;
|
||
|
}
|
||
|
if (content == prevContent) {
|
||
|
continue;
|
||
|
}
|
||
|
prevContent = content;
|
||
|
textContent = do_QueryInterface(content, &mSuccess);
|
||
|
if ( (NS_FAILED(mSuccess) ) || (!textContent) ) {
|
||
|
break;
|
||
|
}
|
||
|
textContent->Text()->AppendTo(mBuffer);
|
||
|
}
|
||
|
else if (nsLayoutAtoms::brFrame == frameType) { // break frame
|
||
|
// Append line separator
|
||
|
mBuffer.Append( (PRUnichar) kLineSeparator);
|
||
|
}
|
||
|
else if (nsLayoutAtoms::directionalFrame == frameType) {
|
||
|
nsDirectionalFrame* dirFrame;
|
||
|
frame->QueryInterface(NS_GET_IID(nsDirectionalFrame),
|
||
|
(void**) &dirFrame);
|
||
|
mBuffer.Append(dirFrame->GetChar() );
|
||
|
}
|
||
|
else { // not text frame
|
||
|
// See the Unicode Bidi Algorithm:
|
||
|
// "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
|
||
|
mBuffer.Append( (PRUnichar) kObjectSubstitute);
|
||
|
}
|
||
|
}
|
||
|
// XXX: TODO: Handle preformatted text ('\n')
|
||
|
mBuffer.ReplaceChar("\t\r\n", kSpace);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::ReorderFrames(nsPresContext* aPresContext,
|
||
|
nsIRenderingContext* aRendContext,
|
||
|
nsIFrame* aFirstChild,
|
||
|
nsIFrame* aNextInFlow,
|
||
|
PRInt32 aChildCount)
|
||
|
{
|
||
|
mLogicalFrames.Clear();
|
||
|
|
||
|
if (NS_SUCCEEDED(InitLogicalArray(aPresContext, aFirstChild, aNextInFlow))
|
||
|
&& (mLogicalFrames.Count() > 1)) {
|
||
|
PRBool bidiEnabled;
|
||
|
// Set bidiEnabled to true if the line is reordered
|
||
|
Reorder(aPresContext, bidiEnabled);
|
||
|
if (bidiEnabled) {
|
||
|
RepositionInlineFrames(aPresContext, aRendContext, aFirstChild, aChildCount);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsBidiPresUtils::Reorder(nsPresContext* aPresContext,
|
||
|
PRBool& aBidiEnabled)
|
||
|
{
|
||
|
aBidiEnabled = PR_FALSE;
|
||
|
PRInt32 count = mLogicalFrames.Count();
|
||
|
|
||
|
if (mArraySize < count) {
|
||
|
mArraySize = count << 1;
|
||
|
if (mLevels) {
|
||
|
delete[] mLevels;
|
||
|
mLevels = nsnull;
|
||
|
}
|
||
|
if (mIndexMap) {
|
||
|
delete[] mIndexMap;
|
||
|
mIndexMap = nsnull;
|
||
|
}
|
||
|
}
|
||
|
if (!mLevels) {
|
||
|
mLevels = new PRUint8[mArraySize];
|
||
|
if (!mLevels) {
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
}
|
||
|
memset(mLevels, 0, sizeof(PRUint8) * mArraySize);
|
||
|
|
||
|
nsIFrame* frame;
|
||
|
PRInt32 i;
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
frame = (nsIFrame*) (mLogicalFrames[i]);
|
||
|
mLevels[i] = NS_GET_EMBEDDING_LEVEL(frame);
|
||
|
}
|
||
|
if (!mIndexMap) {
|
||
|
mIndexMap = new PRInt32[mArraySize];
|
||
|
}
|
||
|
if (!mIndexMap) {
|
||
|
mSuccess = NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
else {
|
||
|
memset(mIndexMap, 0, sizeof(PRUint32) * mArraySize);
|
||
|
|
||
|
mSuccess = mBidiEngine->ReorderVisual(mLevels, count, mIndexMap);
|
||
|
|
||
|
if (NS_SUCCEEDED(mSuccess) ) {
|
||
|
mVisualFrames.Clear();
|
||
|
|
||
|
for (i = 0; i < count; i++) {
|
||
|
mVisualFrames.AppendElement(mLogicalFrames[mIndexMap[i]]);
|
||
|
if (i != mIndexMap[i]) {
|
||
|
aBidiEnabled = PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
} // NS_SUCCEEDED(mSuccess)
|
||
|
} // indexMap
|
||
|
|
||
|
if (NS_FAILED(mSuccess) ) {
|
||
|
aBidiEnabled = PR_FALSE;
|
||
|
}
|
||
|
return mSuccess;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::RepositionInlineFrames(nsPresContext* aPresContext,
|
||
|
nsIRenderingContext* aRendContext,
|
||
|
nsIFrame* aFirstChild,
|
||
|
PRInt32 aChildCount) const
|
||
|
{
|
||
|
PRInt32 count = mVisualFrames.Count();
|
||
|
if (count < 2) {
|
||
|
return;
|
||
|
}
|
||
|
nsIFrame* frame = (nsIFrame*) (mVisualFrames[0]);
|
||
|
PRInt32 i;
|
||
|
|
||
|
PRInt32 ch;
|
||
|
PRInt32 charType;
|
||
|
nscoord width, dWidth, alefWidth, dx;
|
||
|
PRUnichar buf[2] = {ALEF, 0x0000};
|
||
|
|
||
|
PRBool isBidiSystem;
|
||
|
PRUint32 hints = 0;
|
||
|
|
||
|
dWidth = alefWidth = dx = 0;
|
||
|
aRendContext->GetHints(hints);
|
||
|
isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING);
|
||
|
|
||
|
nsRect rect = frame->GetRect();
|
||
|
|
||
|
if (frame != aFirstChild) {
|
||
|
rect.x = aFirstChild->GetPosition().x;
|
||
|
frame->SetPosition(nsPoint(rect.x, rect.y));
|
||
|
}
|
||
|
|
||
|
nsPropertyTable *propTable = aPresContext->PropertyTable();
|
||
|
|
||
|
for (i = 1; i < count; i++) {
|
||
|
|
||
|
ch = 0;
|
||
|
charType = NS_PTR_TO_INT32(propTable->GetProperty((nsIFrame*)mVisualFrames[i], nsLayoutAtoms::charType));
|
||
|
if (CHARTYPE_IS_RTL(charType) ) {
|
||
|
ch = NS_PTR_TO_INT32(propTable->GetProperty(frame,
|
||
|
nsLayoutAtoms::endsInDiacritic));
|
||
|
if (ch) {
|
||
|
if (!alefWidth) {
|
||
|
aRendContext->GetWidth(buf, 1, alefWidth, nsnull);
|
||
|
}
|
||
|
dWidth = 0;
|
||
|
if (isBidiSystem) {
|
||
|
buf[1] = (PRUnichar) ch;
|
||
|
aRendContext->GetWidth(buf, 2, width, nsnull);
|
||
|
dWidth = width - alefWidth;
|
||
|
}
|
||
|
if (dWidth <= 0) {
|
||
|
frame->SetPosition(nsPoint(rect.x + (nscoord)((float)width/8), rect.y));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
frame = (nsIFrame*) (mVisualFrames[i]);
|
||
|
if (ch) {
|
||
|
dx += (rect.width - dWidth);
|
||
|
frame->SetPosition(nsPoint(rect.x + dWidth, frame->GetPosition().y));
|
||
|
} else
|
||
|
frame->SetPosition(nsPoint(rect.XMost(), frame->GetPosition().y));
|
||
|
rect = frame->GetRect();
|
||
|
} // for
|
||
|
|
||
|
if (dx > 0) {
|
||
|
PRInt32 alignRight = NS_GET_BASE_LEVEL(frame);
|
||
|
if (0 == (alignRight & 1) ) {
|
||
|
const nsStyleText* styleText = frame->GetStyleText();
|
||
|
|
||
|
if (NS_STYLE_TEXT_ALIGN_RIGHT == styleText->mTextAlign
|
||
|
|| NS_STYLE_TEXT_ALIGN_MOZ_RIGHT == styleText->mTextAlign) {
|
||
|
alignRight = 1;
|
||
|
}
|
||
|
}
|
||
|
if (alignRight & 1) {
|
||
|
for (i = 0; i < count; i++) {
|
||
|
frame = (nsIFrame*) (mVisualFrames[i]);
|
||
|
frame->SetPosition(frame->GetPosition() + nsPoint(dx, 0));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now adjust inline container frames.
|
||
|
// Example: LTR paragraph
|
||
|
// <p><b>english HEBREW</b> 123</p>
|
||
|
// should be displayed as
|
||
|
// <p><b>english </b>123 <b>WERBEH</b></p>
|
||
|
|
||
|
// We assume that <b></b> rectangle takes all the room from "english" left edge to
|
||
|
// "WERBEH" right edge.
|
||
|
|
||
|
frame = aFirstChild;
|
||
|
for (i = 0; i < aChildCount; i++) {
|
||
|
nsIAtom* frameType = frame->GetType();
|
||
|
if ( (nsLayoutAtoms::inlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::positionedInlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::letterFrame == frameType)
|
||
|
|| (nsLayoutAtoms::blockFrame == frameType) ) {
|
||
|
PRInt32 minX = 0x7FFFFFFF;
|
||
|
PRInt32 maxX = 0;
|
||
|
RepositionContainerFrame(aPresContext, frame, minX, maxX);
|
||
|
}
|
||
|
frame = frame->GetNextSibling();
|
||
|
} // for
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::RepositionContainerFrame(nsPresContext* aPresContext,
|
||
|
nsIFrame* aContainer,
|
||
|
PRInt32& aMinX,
|
||
|
PRInt32& aMaxX) const
|
||
|
{
|
||
|
nsIFrame* frame;
|
||
|
PRInt32 minX = 0x7FFFFFFF;
|
||
|
PRInt32 maxX = 0;
|
||
|
|
||
|
nsIFrame* firstChild = aContainer->GetFirstChild(nsnull);
|
||
|
|
||
|
for (frame = firstChild; frame; frame = frame->GetNextSibling()) {
|
||
|
nsIAtom* frameType = frame->GetType();
|
||
|
if ( (nsLayoutAtoms::inlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::positionedInlineFrame == frameType)
|
||
|
|| (nsLayoutAtoms::letterFrame == frameType)
|
||
|
|| (nsLayoutAtoms::blockFrame == frameType) ) {
|
||
|
RepositionContainerFrame(aPresContext, frame, minX, maxX);
|
||
|
}
|
||
|
else {
|
||
|
nsRect rect = frame->GetRect();
|
||
|
minX = PR_MIN(minX, rect.x);
|
||
|
maxX = PR_MAX(maxX, rect.XMost());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
aMinX = PR_MIN(minX, aMinX);
|
||
|
aMaxX = PR_MAX(maxX, aMaxX);
|
||
|
|
||
|
if (minX < maxX) {
|
||
|
nsRect rect = aContainer->GetRect();
|
||
|
rect.x = minX;
|
||
|
rect.width = maxX - minX;
|
||
|
aContainer->SetRect(rect);
|
||
|
}
|
||
|
|
||
|
// Now adjust all the kids (kid's coordinates are relative to the parent's)
|
||
|
for (frame = firstChild; frame; frame = frame->GetNextSibling()) {
|
||
|
frame->SetPosition(frame->GetPosition() - nsPoint(minX, 0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PRBool
|
||
|
nsBidiPresUtils::EnsureBidiContinuation(nsPresContext* aPresContext,
|
||
|
nsIContent* aContent,
|
||
|
nsIFrame* aFrame,
|
||
|
nsIFrame** aNewFrame,
|
||
|
PRInt32& aFrameIndex)
|
||
|
{
|
||
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
||
|
if (!aNewFrame) {
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
*aNewFrame = nsnull;
|
||
|
|
||
|
if (!aFrame) {
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
if (aFrameIndex + 1 < mLogicalFrames.Count() ) {
|
||
|
nsIFrame* frame = (nsIFrame*)mLogicalFrames[aFrameIndex + 1];
|
||
|
if (frame->GetContent() == aContent) {
|
||
|
*aNewFrame = frame;
|
||
|
++aFrameIndex;
|
||
|
aFrame->SetNextInFlow(nsnull);
|
||
|
frame->SetPrevInFlow(nsnull);
|
||
|
}
|
||
|
}
|
||
|
if (nsnull == *aNewFrame) {
|
||
|
mSuccess = CreateBidiContinuation(aPresContext, aContent, aFrame, aNewFrame);
|
||
|
if (NS_FAILED(mSuccess) ) {
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
}
|
||
|
aPresContext->PropertyTable()->SetProperty(aFrame, nsLayoutAtoms::nextBidi,
|
||
|
(void*) *aNewFrame,
|
||
|
nsnull, nsnull);
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::RemoveBidiContinuation(nsPresContext* aPresContext,
|
||
|
nsIFrame* aFrame,
|
||
|
PRInt32 aFirstIndex,
|
||
|
PRInt32 aLastIndex,
|
||
|
PRInt32& aOffset) const
|
||
|
{
|
||
|
nsIFrame* frame;
|
||
|
PRInt32 index;
|
||
|
nsIFrame* parent = aFrame->GetParent();
|
||
|
|
||
|
aOffset = 0;
|
||
|
|
||
|
for (index = aLastIndex; index > aFirstIndex; index--) {
|
||
|
frame = (nsIFrame*) mLogicalFrames[index];
|
||
|
if (nsLayoutAtoms::directionalFrame == frame->GetType()) {
|
||
|
delete frame;
|
||
|
++aOffset;
|
||
|
}
|
||
|
else {
|
||
|
if (frame->GetStateBits() & NS_FRAME_IS_BIDI) {
|
||
|
// only delete Bidi frames
|
||
|
if (parent) {
|
||
|
parent->RemoveFrame(nsLayoutAtoms::nextBidi, frame);
|
||
|
}
|
||
|
else {
|
||
|
frame->Destroy(aPresContext);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
nsIFrame* thisFramesNextBidiFrame;
|
||
|
nsIFrame* previousFramesNextBidiFrame;
|
||
|
|
||
|
nsPropertyTable *propTable = aPresContext->PropertyTable();
|
||
|
thisFramesNextBidiFrame = NS_STATIC_CAST(nsIFrame*,
|
||
|
propTable->GetProperty(aFrame, nsLayoutAtoms::nextBidi));
|
||
|
|
||
|
if (thisFramesNextBidiFrame) {
|
||
|
// Remove nextBidi property, associated with the current frame
|
||
|
// and with all of its prev-in-flow.
|
||
|
frame = aFrame;
|
||
|
do {
|
||
|
propTable->DeleteProperty(frame, nsLayoutAtoms::nextBidi);
|
||
|
frame = frame->GetPrevInFlow();
|
||
|
if (!frame) {
|
||
|
break;
|
||
|
}
|
||
|
previousFramesNextBidiFrame =
|
||
|
NS_STATIC_CAST(nsIFrame*, propTable->GetProperty(frame,
|
||
|
nsLayoutAtoms::nextBidi));
|
||
|
} while (thisFramesNextBidiFrame == previousFramesNextBidiFrame);
|
||
|
} // if (thisFramesNextBidiFrame)
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
|
||
|
PRUnichar* aText,
|
||
|
PRInt32& aTextLength,
|
||
|
nsCharType aCharType,
|
||
|
PRBool aIsOddLevel,
|
||
|
PRBool aIsBidiSystem)
|
||
|
{
|
||
|
NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
|
||
|
nsresult rv = NS_OK;
|
||
|
// ahmed
|
||
|
//adjusted for correct numeral shaping
|
||
|
PRUint32 bidiOptions = aPresContext->GetBidi();
|
||
|
switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
|
||
|
|
||
|
case IBMBIDI_NUMERAL_HINDI:
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
|
||
|
break;
|
||
|
|
||
|
case IBMBIDI_NUMERAL_ARABIC:
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
|
||
|
break;
|
||
|
|
||
|
case IBMBIDI_NUMERAL_REGULAR:
|
||
|
|
||
|
switch (aCharType) {
|
||
|
|
||
|
case eCharType_EuropeanNumber:
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
|
||
|
break;
|
||
|
|
||
|
case eCharType_ArabicNumber:
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case IBMBIDI_NUMERAL_HINDICONTEXT:
|
||
|
if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
|
||
|
else if (eCharType_EuropeanNumber == aCharType)
|
||
|
HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
|
||
|
break;
|
||
|
|
||
|
case IBMBIDI_NUMERAL_NOMINAL:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PRBool doReverse = PR_FALSE;
|
||
|
PRBool doShape = PR_FALSE;
|
||
|
|
||
|
if (aIsBidiSystem) {
|
||
|
if ( (CHARTYPE_IS_RTL(aCharType)) ^ (aIsOddLevel) )
|
||
|
doReverse = PR_TRUE;
|
||
|
}
|
||
|
else {
|
||
|
if (aIsOddLevel)
|
||
|
doReverse = PR_TRUE;
|
||
|
if (eCharType_RightToLeftArabic == aCharType)
|
||
|
doShape = PR_TRUE;
|
||
|
}
|
||
|
|
||
|
if (doReverse || doShape) {
|
||
|
PRInt32 newLen;
|
||
|
|
||
|
if (mBuffer.Length() < aTextLength) {
|
||
|
mBuffer.SetLength(aTextLength);
|
||
|
if (mBuffer.Length() < aTextLength)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
PRUnichar* buffer = mBuffer.BeginWriting();
|
||
|
|
||
|
if (doReverse) {
|
||
|
rv = mBidiEngine->WriteReverse(aText, aTextLength, buffer,
|
||
|
NSBIDI_DO_MIRRORING, &newLen);
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
aTextLength = newLen;
|
||
|
memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
|
||
|
}
|
||
|
}
|
||
|
if (doShape) {
|
||
|
rv = ArabicShaping(aText, aTextLength, buffer, (PRUint32 *)&newLen,
|
||
|
PR_FALSE, PR_FALSE);
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
aTextLength = newLen;
|
||
|
memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
StripBidiControlCharacters(aText, aTextLength);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::StripBidiControlCharacters(PRUnichar* aText,
|
||
|
PRInt32& aTextLength) const
|
||
|
{
|
||
|
if ( (nsnull == aText) || (aTextLength < 1) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PRInt32 stripLen = 0;
|
||
|
|
||
|
for (PRInt32 i = 0; i < aTextLength; i++) {
|
||
|
// XXX: This silently ignores surrogate characters.
|
||
|
// As of Unicode 4.0, all Bidi control characters are within the BMP.
|
||
|
if (mBidiEngine->IsBidiControl((PRUint32)aText[i])) {
|
||
|
++stripLen;
|
||
|
}
|
||
|
else {
|
||
|
aText[i - stripLen] = aText[i];
|
||
|
}
|
||
|
}
|
||
|
aTextLength -= stripLen;
|
||
|
}
|
||
|
|
||
|
#if 0 // XXX: for the future use ???
|
||
|
void
|
||
|
RemoveDiacritics(PRUnichar* aText,
|
||
|
PRInt32& aTextLength)
|
||
|
{
|
||
|
if (aText && (aTextLength > 0) ) {
|
||
|
PRInt32 offset = 0;
|
||
|
|
||
|
for (PRInt32 i = 0; i < aTextLength && aText[i]; i++) {
|
||
|
if (IS_BIDI_DIACRITIC(aText[i]) ) {
|
||
|
++offset;
|
||
|
continue;
|
||
|
}
|
||
|
aText[i - offset] = aText[i];
|
||
|
}
|
||
|
aTextLength = i - offset;
|
||
|
aText[aTextLength] = 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void
|
||
|
nsBidiPresUtils::CalculateCharType(PRInt32& aOffset,
|
||
|
PRInt32 aCharTypeLimit,
|
||
|
PRInt32& aRunLimit,
|
||
|
PRInt32& aRunLength,
|
||
|
PRInt32& aRunCount,
|
||
|
PRUint8& aCharType,
|
||
|
PRUint8& aPrevCharType) const
|
||
|
|
||
|
{
|
||
|
PRBool strongTypeFound = PR_FALSE;
|
||
|
PRInt32 offset;
|
||
|
nsCharType charType;
|
||
|
|
||
|
aCharType = eCharType_OtherNeutral;
|
||
|
|
||
|
for (offset = aOffset; offset < aCharTypeLimit; offset++) {
|
||
|
// Make sure we give RTL chartype to all characters that would be classified
|
||
|
// as Right-To-Left by a bidi platform.
|
||
|
// (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
|
||
|
if (IS_HEBREW_CHAR(mBuffer[offset]) ) {
|
||
|
charType = eCharType_RightToLeft;
|
||
|
}
|
||
|
else if (IS_ARABIC_ALPHABETIC(mBuffer[offset]) ) {
|
||
|
charType = eCharType_RightToLeftArabic;
|
||
|
}
|
||
|
else {
|
||
|
mBidiEngine->GetCharTypeAt(offset, &charType);
|
||
|
}
|
||
|
|
||
|
if (!CHARTYPE_IS_WEAK(charType) ) {
|
||
|
|
||
|
if (strongTypeFound
|
||
|
&& (charType != aPrevCharType)
|
||
|
&& (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
|
||
|
// Stop at this point to ensure uni-directionality of the text
|
||
|
// (from platform's point of view).
|
||
|
// Also, don't mix Arabic and Hebrew content (since platform may
|
||
|
// provide BIDI support to one of them only).
|
||
|
aRunLength = offset - aOffset;
|
||
|
aRunLimit = offset;
|
||
|
++aRunCount;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( (eCharType_RightToLeftArabic == aPrevCharType
|
||
|
|| eCharType_ArabicNumber == aPrevCharType)
|
||
|
&& eCharType_EuropeanNumber == charType) {
|
||
|
charType = eCharType_ArabicNumber;
|
||
|
}
|
||
|
|
||
|
// Set PrevCharType to the last strong type in this frame
|
||
|
// (for correct numeric shaping)
|
||
|
aPrevCharType = charType;
|
||
|
|
||
|
strongTypeFound = PR_TRUE;
|
||
|
aCharType = charType;
|
||
|
}
|
||
|
}
|
||
|
aOffset = offset;
|
||
|
}
|
||
|
|
||
|
nsresult nsBidiPresUtils::GetBidiEngine(nsBidi** aBidiEngine)
|
||
|
{
|
||
|
nsresult rv = NS_ERROR_FAILURE;
|
||
|
if (mBidiEngine) {
|
||
|
*aBidiEngine = mBidiEngine;
|
||
|
rv = NS_OK;
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
nsresult nsBidiPresUtils::RenderText(const PRUnichar* aText,
|
||
|
PRInt32 aLength,
|
||
|
nsBidiDirection aBaseDirection,
|
||
|
nsPresContext* aPresContext,
|
||
|
nsIRenderingContext& aRenderingContext,
|
||
|
nscoord aX,
|
||
|
nscoord aY,
|
||
|
nsBidiPositionResolve* aPosResolve,
|
||
|
PRInt32 aPosResolveCount)
|
||
|
{
|
||
|
NS_ASSERTION((aPosResolve == nsnull) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
|
||
|
|
||
|
PRInt32 runCount;
|
||
|
|
||
|
mBuffer.Assign(aText, aLength);
|
||
|
|
||
|
nsresult rv = mBidiEngine->SetPara(mBuffer.get(), aLength, aBaseDirection, nsnull);
|
||
|
if (NS_FAILED(rv))
|
||
|
return rv;
|
||
|
|
||
|
rv = mBidiEngine->CountRuns(&runCount);
|
||
|
if (NS_FAILED(rv))
|
||
|
return rv;
|
||
|
|
||
|
nscoord width, xEndRun, xStartText = aX;
|
||
|
PRBool isRTL = PR_FALSE;
|
||
|
PRInt32 i, start, limit, length;
|
||
|
PRUint32 visualStart = 0;
|
||
|
PRUint8 charType;
|
||
|
PRUint8 prevType = eCharType_LeftToRight;
|
||
|
nsBidiLevel level;
|
||
|
|
||
|
PRUint32 hints = 0;
|
||
|
aRenderingContext.GetHints(hints);
|
||
|
PRBool isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING);
|
||
|
|
||
|
for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
|
||
|
{
|
||
|
aPosResolve[nPosResolve].visualIndex = kNotFound;
|
||
|
aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < runCount; i++) {
|
||
|
rv = mBidiEngine->GetVisualRun(i, &start, &length, &aBaseDirection);
|
||
|
if (NS_FAILED(rv))
|
||
|
return rv;
|
||
|
|
||
|
rv = mBidiEngine->GetLogicalRun(start, &limit, &level);
|
||
|
if (NS_FAILED(rv))
|
||
|
return rv;
|
||
|
|
||
|
PRInt32 subRunLength = limit - start;
|
||
|
PRInt32 lineOffset = start;
|
||
|
PRInt32 typeLimit = PR_MIN(limit, aLength);
|
||
|
PRInt32 subRunCount = 1;
|
||
|
PRInt32 subRunLimit = typeLimit;
|
||
|
|
||
|
/*
|
||
|
* If |level| is even, i.e. the direction of the run is left-to-right, we
|
||
|
* render the subruns from left to right and increment the x-coordinate
|
||
|
* |aX| by the width of each subrun after rendering.
|
||
|
*
|
||
|
* If |level| is odd, i.e. the direction of the run is right-to-left, we
|
||
|
* render the subruns from right to left. We begin by incrementing |aX| by
|
||
|
* the width of the whole run, and then decrement it by the width of each
|
||
|
* subrun before rendering. After rendering all the subruns, we restore the
|
||
|
* x-coordinate of the end of the run for the start of the next run.
|
||
|
*/
|
||
|
if (level & 1) {
|
||
|
aRenderingContext.GetWidth(aText + start, subRunLength, width, nsnull);
|
||
|
aX += width;
|
||
|
xEndRun = aX;
|
||
|
}
|
||
|
|
||
|
while (subRunCount > 0) {
|
||
|
// CalculateCharType can increment subRunCount if the run
|
||
|
// contains mixed character types
|
||
|
CalculateCharType(lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
|
||
|
|
||
|
if (eCharType_RightToLeftArabic == charType) {
|
||
|
isBidiSystem = (hints & NS_RENDERING_HINT_ARABIC_SHAPING);
|
||
|
}
|
||
|
if (isBidiSystem && (CHARTYPE_IS_RTL(charType) ^ isRTL) ) {
|
||
|
// set reading order into DC
|
||
|
isRTL = !isRTL;
|
||
|
aRenderingContext.SetRightToLeftText(isRTL);
|
||
|
}
|
||
|
|
||
|
nsAutoString runVisualText;
|
||
|
runVisualText.Assign(aText + start, subRunLength);
|
||
|
if (runVisualText.Length() < subRunLength)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
|
||
|
(nsCharType)charType, level & 1,
|
||
|
isBidiSystem);
|
||
|
|
||
|
aRenderingContext.GetWidth(runVisualText.get(), subRunLength, width, nsnull);
|
||
|
if (level & 1) {
|
||
|
aX -= width;
|
||
|
}
|
||
|
aRenderingContext.DrawString(runVisualText.get(), subRunLength, aX, aY, width);
|
||
|
|
||
|
/*
|
||
|
* The caller may request to calculate the visual position of one
|
||
|
* or more characters.
|
||
|
*/
|
||
|
for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
|
||
|
{
|
||
|
nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
|
||
|
/*
|
||
|
* Did we already resolve this position's visual metric? If so, skip.
|
||
|
*/
|
||
|
if (posResolve->visualLeftTwips != kNotFound)
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* First find out if the logical position is within this run.
|
||
|
*/
|
||
|
if (start <= posResolve->logicalIndex &&
|
||
|
start + subRunLength > posResolve->logicalIndex) {
|
||
|
/*
|
||
|
* If this run is only one character long, we have an easy case:
|
||
|
* the visual position is the x-coord of the start of the run
|
||
|
* less the x-coord of the start of the whole text (saved in xStartText).
|
||
|
*/
|
||
|
if (subRunLength == 1) {
|
||
|
posResolve->visualIndex = visualStart;
|
||
|
posResolve->visualLeftTwips = aX - xStartText;
|
||
|
}
|
||
|
/*
|
||
|
* Otherwise, we need to measure the width of the run's part
|
||
|
* which is to the visual left of the index.
|
||
|
* In other words, the run is broken in two, around the logical index,
|
||
|
* and we measure the part which is visually left.
|
||
|
* If the run is right-to-left, this part will span from after the index
|
||
|
* up to the end of the run; if it is left-to-right, this part will span
|
||
|
* from the start of the run up to (and inclduing) the character before the index.
|
||
|
*/
|
||
|
else {
|
||
|
nscoord subWidth;
|
||
|
// The position in the text where this run's "left part" begins.
|
||
|
const PRUnichar* visualLeftPart;
|
||
|
if (level & 1) {
|
||
|
// One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
|
||
|
posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
|
||
|
// Skipping to the "left part".
|
||
|
visualLeftPart = aText + posResolve->logicalIndex + 1;
|
||
|
}
|
||
|
else {
|
||
|
posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
|
||
|
// Skipping to the "left part".
|
||
|
visualLeftPart = aText + start;
|
||
|
}
|
||
|
// The delta between the start of the run and the left part's end.
|
||
|
PRInt32 visualLeftLength = posResolve->visualIndex - visualStart;
|
||
|
aRenderingContext.GetWidth(visualLeftPart,
|
||
|
visualLeftLength,
|
||
|
subWidth, nsnull);
|
||
|
posResolve->visualLeftTwips = aX + subWidth - xStartText;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(level & 1)) {
|
||
|
aX += width;
|
||
|
}
|
||
|
|
||
|
--subRunCount;
|
||
|
start = lineOffset;
|
||
|
subRunLimit = typeLimit;
|
||
|
subRunLength = typeLimit - lineOffset;
|
||
|
} // while
|
||
|
if (level & 1) {
|
||
|
aX = xEndRun;
|
||
|
}
|
||
|
|
||
|
visualStart += length;
|
||
|
} // for
|
||
|
|
||
|
// Restore original reading order
|
||
|
if (isRTL) {
|
||
|
aRenderingContext.SetRightToLeftText(PR_FALSE);
|
||
|
}
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult
|
||
|
nsBidiPresUtils::ReorderUnicodeText(PRUnichar* aText,
|
||
|
PRInt32& aTextLength,
|
||
|
nsCharType aCharType,
|
||
|
PRBool aIsOddLevel,
|
||
|
PRBool aIsBidiSystem)
|
||
|
{
|
||
|
NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
|
||
|
nsresult rv = NS_OK;
|
||
|
PRBool doReverse = PR_FALSE;
|
||
|
|
||
|
if (aIsBidiSystem) {
|
||
|
if ( (CHARTYPE_IS_RTL(aCharType)) ^ (aIsOddLevel) )
|
||
|
doReverse = PR_TRUE;
|
||
|
}
|
||
|
else {
|
||
|
if (aIsOddLevel)
|
||
|
doReverse = PR_TRUE;
|
||
|
}
|
||
|
|
||
|
if (doReverse) {
|
||
|
PRInt32 newLen;
|
||
|
|
||
|
if (mBuffer.Length() < aTextLength) {
|
||
|
mBuffer.SetLength(aTextLength);
|
||
|
if (mBuffer.Length() < aTextLength)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
PRUnichar* buffer = mBuffer.BeginWriting();
|
||
|
|
||
|
if (doReverse) {
|
||
|
rv = mBidiEngine->WriteReverse(aText, aTextLength, buffer,
|
||
|
NSBIDI_DO_MIRRORING, &newLen);
|
||
|
if (NS_SUCCEEDED(rv) ) {
|
||
|
aTextLength = newLen;
|
||
|
memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
#endif // IBMBIDI
|