Back to index

lightning-sunbird  0.9+nobinonly
nsBidiPresUtils.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifdef IBMBIDI
00039 
00040 #include "nsBidiPresUtils.h"
00041 #include "nsITextContent.h"
00042 #include "nsTextFragment.h"
00043 #include "nsLayoutAtoms.h"
00044 #include "nsPresContext.h"
00045 #include "nsIRenderingContext.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsFrameManager.h"
00048 #include "nsBidiFrames.h"
00049 #include "nsBidiUtils.h"
00050 
00051 static const PRUnichar kSpace            = 0x0020;
00052 static const PRUnichar kLineSeparator    = 0x2028;
00053 static const PRUnichar kObjectSubstitute = 0xFFFC;
00054 static const PRUnichar kLRE              = 0x202A;
00055 static const PRUnichar kRLE              = 0x202B;
00056 static const PRUnichar kLRO              = 0x202D;
00057 static const PRUnichar kRLO              = 0x202E;
00058 static const PRUnichar kPDF              = 0x202C;
00059 static const PRUnichar ALEF              = 0x05D0;
00060 
00061 #define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
00062 // Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
00063 
00064 nsresult
00065 NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsIFrame** aResult);
00066 nsresult
00067 NS_NewDirectionalFrame(nsIFrame** aNewFrame, PRUnichar aChar);
00068 
00069 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
00070                                      mIndexMap(nsnull),
00071                                      mLevels(nsnull),
00072                                      mSuccess(NS_ERROR_FAILURE),
00073                                      mBidiEngine(nsnull)
00074 {
00075   mBidiEngine = new nsBidi();
00076   if (mBidiEngine && mContentToFrameIndex.Init()) {
00077     mSuccess = NS_OK;
00078   }
00079 }
00080 
00081 nsBidiPresUtils::~nsBidiPresUtils()
00082 {
00083   if (mLevels) {
00084     delete[] mLevels;
00085   }
00086   if (mIndexMap) {
00087     delete[] mIndexMap;
00088   }
00089   delete mBidiEngine;
00090 }
00091 
00092 PRBool
00093 nsBidiPresUtils::IsSuccessful() const
00094 { 
00095   return NS_SUCCEEDED(mSuccess); 
00096 }
00097 
00098 /* Some helper methods for Resolve() */
00099 
00100 static nsresult
00101 CreateBidiContinuation(nsPresContext* aPresContext,
00102                        nsIContent*     aContent,
00103                        nsIFrame*       aFrame,
00104                        nsIFrame**      aNewFrame)
00105 {
00106   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00107 
00108   *aNewFrame = nsnull;
00109 
00110   NS_PRECONDITION(aFrame, "null ptr");
00111 
00112   nsIPresShell *presShell = aPresContext->PresShell();
00113 
00114   NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
00115 
00116   NS_NewContinuingTextFrame(presShell, aNewFrame);
00117   if (!(*aNewFrame) ) {
00118     return NS_ERROR_OUT_OF_MEMORY;
00119   }
00120   nsStyleContext* styleContext = aFrame->GetStyleContext();
00121 
00122   NS_ASSERTION(styleContext, "Frame has no styleContext in nsBidiPresUtils::CreateBidiContinuation");
00123   
00124   nsIFrame* parent = aFrame->GetParent();
00125   NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
00126 
00127   (*aNewFrame)->Init(aPresContext, aContent, parent, styleContext, nsnull);
00128 
00129   // XXX: TODO: Instead, create and insert entire frame list
00130   (*aNewFrame)->SetNextSibling(nsnull);
00131 
00132   // The list name nsLayoutAtoms::nextBidi would indicate we don't want reflow
00133   parent->InsertFrames(nsLayoutAtoms::nextBidi, aFrame, *aNewFrame);
00134 
00135   return NS_OK;
00136 }
00137 /*
00138  * Overview of the implementation of Resolve():
00139  *
00140  *  Walk through the descendants of aBlockFrame and build:
00141  *   * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
00142  *   * mBuffer: an nsAutoString containing a representation of
00143  *     the content of the frames.
00144  *     In the case of text frames, this is the actual text context of the
00145  *     frames, but some other elements are represented in a symbolic form which
00146  *     will make the Unicode Bidi Algorithm give the correct results.
00147  *     Bidi embeddings and overrides set by CSS or <bdo> elements are
00148  *     represented by the corresponding Unicode control characters.
00149  *     <br> elements are represented by U+2028 LINE SEPARATOR
00150  *     Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
00151  *     CHARACTER
00152  *
00153  *  Then pass mBuffer to the Bidi engine for resolving of embedding levels
00154  *  by nsBidi::SetPara() and division into directional runs by
00155  *  nsBidi::CountRuns().
00156  *
00157  *  Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
00158  *  correlate them with the frames indexed in mLogicalArray, setting the
00159  *  baseLevel, embeddingLevel, and charType properties according to the results
00160  *  returned by the Bidi engine and CalculateCharType().
00161  *
00162  *  The rendering layer requires each text frame to contain text in only one
00163  *  direction and of only one character type, so we may need to call
00164  *  EnsureBidiContinuation() to split frames. We may also need to call
00165  *  RemoveBidiContinuation() to delete frames created by
00166  *  EnsureBidiContinuation() in previous reflows.
00167  */
00168 nsresult
00169 nsBidiPresUtils::Resolve(nsPresContext* aPresContext,
00170                          nsIFrame*       aBlockFrame,
00171                          nsIFrame*       aFirstChild,
00172                          PRBool&         aForceReflow,
00173                          PRBool          aIsVisualFormControl)
00174 {
00175   aForceReflow = PR_FALSE;
00176   mLogicalFrames.Clear();
00177   mContentToFrameIndex.Clear();
00178 
00179   // handle bidi-override being set on the block itself before calling
00180   // InitLogicalArray.
00181   const nsStyleVisibility* vis = aBlockFrame->GetStyleVisibility();
00182   const nsStyleTextReset* text = aBlockFrame->GetStyleTextReset();
00183 
00184   if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
00185     nsresult rv = NS_OK;
00186     nsIFrame *directionalFrame = nsnull;
00187     if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
00188       rv = NS_NewDirectionalFrame(&directionalFrame, kRLO);
00189     }
00190     else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
00191       rv = NS_NewDirectionalFrame(&directionalFrame, kLRO);
00192     }
00193     if (directionalFrame && NS_SUCCEEDED(rv)) {
00194       mLogicalFrames.AppendElement(directionalFrame);
00195     }
00196   }
00197   mSuccess = InitLogicalArray(aPresContext, aFirstChild, nsnull, PR_TRUE);
00198   if (text->mUnicodeBidi == NS_STYLE_UNICODE_BIDI_OVERRIDE) {
00199     nsIFrame *directionalFrame = nsnull;
00200     nsresult rv = NS_NewDirectionalFrame(&directionalFrame, kPDF);
00201     if (directionalFrame && NS_SUCCEEDED(rv)) {
00202       mLogicalFrames.AppendElement(directionalFrame);
00203     }
00204   }
00205   if (NS_FAILED(mSuccess) ) {
00206     return mSuccess;
00207   }
00208 
00209   CreateBlockBuffer(aPresContext);
00210 
00211   PRInt32 bufferLength = mBuffer.Length();
00212 
00213   if (bufferLength < 1) {
00214     mSuccess = NS_OK;
00215     return mSuccess;
00216   }
00217   PRInt32 runCount;
00218   PRUint8 embeddingLevel;
00219 
00220   nsBidiLevel paraLevel = embeddingLevel =
00221     (NS_STYLE_DIRECTION_RTL == vis->mDirection)
00222     ? NSBIDI_RTL : NSBIDI_LTR;
00223 
00224   mSuccess = mBidiEngine->SetPara(mBuffer.get(), bufferLength, paraLevel, nsnull);
00225   if (NS_FAILED(mSuccess) ) {
00226       return mSuccess;
00227   }
00228 
00229   PRBool isVisual;
00230   if (aIsVisualFormControl) {
00231     isVisual = PR_FALSE;
00232   } else {
00233     isVisual = aPresContext->IsVisualMode();
00234   }
00235   mSuccess = mBidiEngine->CountRuns(&runCount);
00236   if (NS_FAILED(mSuccess) ) {
00237     return mSuccess;
00238   }
00239   PRInt32                  runLength      = 0;
00240   PRInt32                  fragmentLength = 0;
00241   PRInt32                  temp;
00242   PRInt32                  frameIndex     = -1;
00243   PRInt32                  frameCount     = mLogicalFrames.Count();
00244   PRInt32                  contentOffset  = 0;   // offset within current frame
00245   PRInt32                  lineOffset     = 0;   // offset within mBuffer
00246   PRInt32                  logicalLimit   = 0;
00247   PRInt32                  numRun         = -1;
00248   PRUint8                  charType;
00249   PRUint8                  prevType       = eCharType_LeftToRight;
00250   PRBool                   isTextFrame    = PR_FALSE;
00251   nsIFrame*                frame = nsnull;
00252   nsIFrame*                nextBidi;
00253   nsIContent*              content = nsnull;
00254   nsCOMPtr<nsITextContent> textContent;
00255   const nsTextFragment*    fragment;
00256   nsIAtom*                 frameType = nsnull;
00257 
00258   nsPropertyTable *propTable = aPresContext->PropertyTable();
00259 
00260   for (; ;) {
00261     if (fragmentLength <= 0) {
00262       if (++frameIndex >= frameCount) {
00263         break;
00264       }
00265       contentOffset = 0;
00266       
00267       frame = (nsIFrame*) (mLogicalFrames[frameIndex]);
00268       frameType = frame->GetType();
00269       if (nsLayoutAtoms::textFrame == frameType) {
00270         content = frame->GetContent();
00271         if (!content) {
00272           mSuccess = NS_OK;
00273           break;
00274         }
00275         textContent = do_QueryInterface(content, &mSuccess);
00276         if (NS_FAILED(mSuccess) || (!textContent) ) {
00277           break;
00278         }
00279         fragment = textContent->Text();
00280         if (!fragment) {
00281           mSuccess = NS_ERROR_FAILURE;
00282           break;
00283         }
00284         fragmentLength = fragment->GetLength();
00285         isTextFrame = PR_TRUE;
00286       } // if text frame
00287       else {
00288         isTextFrame = PR_FALSE;
00289         fragmentLength = 1;
00290       }
00291     } // if (fragmentLength <= 0)
00292     if (runLength <= 0) {
00293       if (++numRun >= runCount) {
00294         break;
00295       }
00296       lineOffset = logicalLimit;
00297       if (NS_FAILED(mBidiEngine->GetLogicalRun(
00298               lineOffset, &logicalLimit, &embeddingLevel) ) ) {
00299         break;
00300       }
00301       runLength = logicalLimit - lineOffset;
00302       if (isVisual) {
00303         embeddingLevel = paraLevel;
00304       }
00305     } // if (runLength <= 0)
00306 
00307     if (nsLayoutAtoms::directionalFrame == frameType) {
00308       delete frame;
00309       ++lineOffset;
00310     }
00311     else {
00312       propTable->SetProperty(frame, nsLayoutAtoms::embeddingLevel,
00313                              NS_INT32_TO_PTR(embeddingLevel), nsnull, nsnull);
00314       propTable->SetProperty(frame, nsLayoutAtoms::baseLevel,
00315                              NS_INT32_TO_PTR(paraLevel), nsnull, nsnull);
00316       if (isTextFrame) {
00317         PRInt32 typeLimit = PR_MIN(logicalLimit, lineOffset + fragmentLength);
00318         CalculateCharType(lineOffset, typeLimit, logicalLimit, runLength,
00319                            runCount, charType, prevType);
00320         // IBMBIDI - Egypt - Start
00321         propTable->SetProperty(frame, nsLayoutAtoms::charType,
00322                                NS_INT32_TO_PTR(charType), nsnull, nsnull);
00323         // IBMBIDI - Egypt - End
00324 
00325         if ( (runLength > 0) && (runLength < fragmentLength) ) {
00326           if (!EnsureBidiContinuation(aPresContext, content, frame,
00327                                       &nextBidi, frameIndex) ) {
00328             break;
00329           }
00330           frame->AdjustOffsetsForBidi(contentOffset, contentOffset + runLength);
00331           frame = nextBidi;
00332           contentOffset += runLength;
00333         } // if (runLength < fragmentLength)
00334         else {
00335           frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
00336           PRInt32 newIndex = 0;
00337           mContentToFrameIndex.Get(content, &newIndex);
00338           if (newIndex > frameIndex) {
00339             RemoveBidiContinuation(aPresContext, frame,
00340                                    frameIndex, newIndex, temp);
00341             aForceReflow = PR_TRUE;
00342             runLength -= temp;
00343             fragmentLength -= temp;
00344             lineOffset += temp;
00345             frameIndex = newIndex;
00346           }
00347         }
00348       } // isTextFrame
00349       else {
00350         ++lineOffset;
00351       }
00352     } // not directionalFrame
00353     temp = runLength;
00354     runLength -= fragmentLength;
00355     fragmentLength -= temp;
00356   } // for
00357   return mSuccess;
00358 }
00359 
00360 nsresult
00361 nsBidiPresUtils::InitLogicalArray(nsPresContext* aPresContext,
00362                                   nsIFrame*       aCurrentFrame,
00363                                   nsIFrame*       aNextInFlow,
00364                                   PRBool          aAddMarkers)
00365 {
00366   nsIFrame*             frame;
00367   nsIFrame*             directionalFrame;
00368   nsresult              rv;
00369   nsresult              res = NS_OK;
00370 
00371   for (frame = aCurrentFrame;
00372        frame && frame != aNextInFlow;
00373        frame = frame->GetNextSibling()) {
00374     rv = NS_ERROR_FAILURE;
00375     const nsStyleDisplay* display = frame->GetStyleDisplay();
00376     
00377     if (aAddMarkers && !display->IsBlockLevel() ) {
00378       const nsStyleVisibility* vis = frame->GetStyleVisibility();
00379       const nsStyleTextReset* text = frame->GetStyleTextReset();
00380       switch (text->mUnicodeBidi) {
00381         case NS_STYLE_UNICODE_BIDI_NORMAL:
00382           break;
00383         case NS_STYLE_UNICODE_BIDI_EMBED:
00384           if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
00385             rv = NS_NewDirectionalFrame(&directionalFrame, kRLE);
00386           }
00387           else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
00388             rv = NS_NewDirectionalFrame(&directionalFrame, kLRE);
00389           }
00390           break;
00391         case NS_STYLE_UNICODE_BIDI_OVERRIDE:
00392           if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
00393             rv = NS_NewDirectionalFrame(&directionalFrame, kRLO);
00394           }
00395           else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
00396             rv = NS_NewDirectionalFrame(&directionalFrame, kLRO);
00397           }
00398           break;
00399       }
00400       if (NS_SUCCEEDED(rv) ) {
00401         mLogicalFrames.AppendElement(directionalFrame);
00402       }
00403     }
00404 
00405     nsIAtom* frameType = frame->GetType();
00406 
00407     if ( (!display->IsBlockLevel() )
00408         && ( (nsLayoutAtoms::inlineFrame == frameType)
00409           || (nsLayoutAtoms::positionedInlineFrame == frameType)
00410           || (nsLayoutAtoms::letterFrame == frameType)
00411           || (nsLayoutAtoms::blockFrame == frameType) ) ) {
00412       nsIFrame* kid = frame->GetFirstChild(nsnull);
00413       res = InitLogicalArray(aPresContext, kid, aNextInFlow, aAddMarkers);
00414     }
00415     else {
00416       /* Bidi leaf frame: add the frame to the mLogicalFrames array,
00417        * and add its index to the mContentToFrameIndex hashtable. This
00418        * will be used in RemoveBidiContinuation() to identify the last
00419        * frame in the array with a given content.
00420        */
00421       nsIContent* content = frame->GetContent();
00422       if (content) {
00423         mContentToFrameIndex.Put(content, mLogicalFrames.Count());
00424       }
00425       mLogicalFrames.AppendElement(frame);
00426     }
00427 
00428     // If the element is attributed by dir, indicate direction pop (add PDF frame)
00429     if (NS_SUCCEEDED(rv) ) {
00430       rv = NS_NewDirectionalFrame(&directionalFrame, kPDF);
00431       if (NS_SUCCEEDED(rv) ) {
00432         mLogicalFrames.AppendElement(directionalFrame);
00433       }
00434     }
00435   } // for
00436   return res;       
00437 }
00438 
00439 void
00440 nsBidiPresUtils::CreateBlockBuffer(nsPresContext* aPresContext)
00441 {
00442   mBuffer.SetLength(0);
00443 
00444   nsIFrame*                 frame;
00445   nsIContent*               prevContent = nsnull;
00446   nsCOMPtr<nsITextContent>  textContent;
00447   PRUint32                  i;
00448   PRUint32                  count = mLogicalFrames.Count();
00449 
00450   for (i = 0; i < count; i++) {
00451     frame = (nsIFrame*) (mLogicalFrames[i]);
00452     nsIAtom* frameType = frame->GetType();
00453 
00454     if (nsLayoutAtoms::textFrame == frameType) {
00455       nsIContent* content = frame->GetContent();
00456       if (!content) {
00457         mSuccess = NS_OK;
00458         break;
00459       }
00460       if (content == prevContent) {
00461         continue;
00462       }
00463       prevContent = content;
00464       textContent = do_QueryInterface(content, &mSuccess);
00465       if ( (NS_FAILED(mSuccess) ) || (!textContent) ) {
00466         break;
00467       }
00468       textContent->Text()->AppendTo(mBuffer);
00469     }
00470     else if (nsLayoutAtoms::brFrame == frameType) { // break frame
00471       // Append line separator
00472       mBuffer.Append( (PRUnichar) kLineSeparator);
00473     }
00474     else if (nsLayoutAtoms::directionalFrame == frameType) {
00475       nsDirectionalFrame* dirFrame;
00476       frame->QueryInterface(NS_GET_IID(nsDirectionalFrame),
00477                             (void**) &dirFrame);
00478       mBuffer.Append(dirFrame->GetChar() );
00479     }
00480     else { // not text frame
00481       // See the Unicode Bidi Algorithm:
00482       // "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
00483       mBuffer.Append( (PRUnichar) kObjectSubstitute);
00484     }
00485   }
00486   // XXX: TODO: Handle preformatted text ('\n')
00487   mBuffer.ReplaceChar("\t\r\n", kSpace);
00488 }
00489 
00490 void
00491 nsBidiPresUtils::ReorderFrames(nsPresContext*      aPresContext,
00492                                nsIRenderingContext* aRendContext,
00493                                nsIFrame*            aFirstChild,
00494                                nsIFrame*            aNextInFlow,
00495                                PRInt32              aChildCount)
00496 {
00497   mLogicalFrames.Clear();
00498 
00499   if (NS_SUCCEEDED(InitLogicalArray(aPresContext, aFirstChild, aNextInFlow))
00500       && (mLogicalFrames.Count() > 1)) {
00501     PRBool bidiEnabled;
00502     // Set bidiEnabled to true if the line is reordered
00503     Reorder(aPresContext, bidiEnabled);
00504     if (bidiEnabled) {
00505       RepositionInlineFrames(aPresContext, aRendContext, aFirstChild, aChildCount);
00506     }
00507   }
00508 }
00509 
00510 nsresult
00511 nsBidiPresUtils::Reorder(nsPresContext* aPresContext,
00512                          PRBool&         aBidiEnabled)
00513 {
00514   aBidiEnabled = PR_FALSE;
00515   PRInt32 count = mLogicalFrames.Count();
00516 
00517   if (mArraySize < count) {
00518     mArraySize = count << 1;
00519     if (mLevels) {
00520       delete[] mLevels;
00521       mLevels = nsnull;
00522     }
00523     if (mIndexMap) {
00524       delete[] mIndexMap;
00525       mIndexMap = nsnull;
00526     }
00527   }
00528   if (!mLevels) {
00529     mLevels = new PRUint8[mArraySize];
00530     if (!mLevels) {
00531       return NS_ERROR_OUT_OF_MEMORY;
00532     }
00533   }
00534   memset(mLevels, 0, sizeof(PRUint8) * mArraySize);
00535 
00536   nsIFrame* frame;
00537   PRInt32   i;
00538 
00539   for (i = 0; i < count; i++) {
00540     frame = (nsIFrame*) (mLogicalFrames[i]);
00541     mLevels[i] = NS_GET_EMBEDDING_LEVEL(frame);
00542   }
00543   if (!mIndexMap) {
00544     mIndexMap = new PRInt32[mArraySize];
00545   }
00546   if (!mIndexMap) {
00547     mSuccess = NS_ERROR_OUT_OF_MEMORY;
00548   }
00549   else {
00550     memset(mIndexMap, 0, sizeof(PRUint32) * mArraySize);
00551 
00552     mSuccess = mBidiEngine->ReorderVisual(mLevels, count, mIndexMap);
00553 
00554     if (NS_SUCCEEDED(mSuccess) ) {
00555       mVisualFrames.Clear();
00556 
00557       for (i = 0; i < count; i++) {
00558         mVisualFrames.AppendElement(mLogicalFrames[mIndexMap[i]]);
00559         if (i != mIndexMap[i]) {
00560           aBidiEnabled = PR_TRUE;
00561         }
00562       }
00563     } // NS_SUCCEEDED(mSuccess)
00564   } // indexMap
00565 
00566   if (NS_FAILED(mSuccess) ) {
00567     aBidiEnabled = PR_FALSE;
00568   }
00569   return mSuccess;
00570 }
00571 
00572 void
00573 nsBidiPresUtils::RepositionInlineFrames(nsPresContext*      aPresContext,
00574                                         nsIRenderingContext* aRendContext,
00575                                         nsIFrame*            aFirstChild,
00576                                         PRInt32              aChildCount) const
00577 {
00578   PRInt32 count = mVisualFrames.Count();
00579   if (count < 2) {
00580     return;
00581   }
00582   nsIFrame* frame = (nsIFrame*) (mVisualFrames[0]);
00583   PRInt32 i;
00584 
00585   PRInt32 ch;
00586   PRInt32 charType;
00587   nscoord width, dWidth, alefWidth, dx;
00588   PRUnichar buf[2] = {ALEF, 0x0000};
00589 
00590   PRBool isBidiSystem;
00591   PRUint32 hints = 0;
00592 
00593   dWidth = alefWidth = dx = 0;
00594   aRendContext->GetHints(hints);
00595   isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING);
00596 
00597   nsRect rect = frame->GetRect();
00598 
00599   if (frame != aFirstChild) {
00600     rect.x = aFirstChild->GetPosition().x;
00601     frame->SetPosition(nsPoint(rect.x, rect.y));
00602   }
00603 
00604   nsPropertyTable *propTable = aPresContext->PropertyTable();
00605 
00606   for (i = 1; i < count; i++) {
00607 
00608     ch = 0;
00609     charType = NS_PTR_TO_INT32(propTable->GetProperty((nsIFrame*)mVisualFrames[i], nsLayoutAtoms::charType));
00610     if (CHARTYPE_IS_RTL(charType) ) {
00611       ch = NS_PTR_TO_INT32(propTable->GetProperty(frame,
00612                                              nsLayoutAtoms::endsInDiacritic));
00613       if (ch) {
00614         if (!alefWidth) {
00615           aRendContext->GetWidth(buf, 1, alefWidth, nsnull);
00616         }
00617         dWidth = 0;
00618         if (isBidiSystem) {
00619           buf[1] = (PRUnichar) ch;
00620           aRendContext->GetWidth(buf, 2, width, nsnull);
00621           dWidth = width - alefWidth;
00622         }
00623         if (dWidth <= 0) {
00624           frame->SetPosition(nsPoint(rect.x + (nscoord)((float)width/8), rect.y));
00625         }
00626       }
00627     }
00628     frame = (nsIFrame*) (mVisualFrames[i]);
00629     if (ch) {
00630       dx += (rect.width - dWidth);
00631       frame->SetPosition(nsPoint(rect.x + dWidth, frame->GetPosition().y));
00632     } else
00633       frame->SetPosition(nsPoint(rect.XMost(), frame->GetPosition().y));
00634     rect = frame->GetRect();
00635   } // for
00636 
00637   if (dx > 0) {
00638     PRInt32 alignRight = NS_GET_BASE_LEVEL(frame);
00639     if (0 == (alignRight & 1) ) {
00640       const nsStyleText* styleText = frame->GetStyleText();
00641       
00642       if (NS_STYLE_TEXT_ALIGN_RIGHT == styleText->mTextAlign
00643           || NS_STYLE_TEXT_ALIGN_MOZ_RIGHT == styleText->mTextAlign) {
00644         alignRight = 1;
00645       }
00646     }
00647     if (alignRight & 1) {
00648       for (i = 0; i < count; i++) {
00649         frame = (nsIFrame*) (mVisualFrames[i]);
00650         frame->SetPosition(frame->GetPosition() + nsPoint(dx, 0));
00651       }
00652     }
00653   }
00654   
00655   // Now adjust inline container frames.
00656   // Example: LTR paragraph 
00657   //                <p><b>english HEBREW</b> 123</p>
00658   // should be displayed as
00659   //                <p><b>english </b>123 <b>WERBEH</b></p>
00660 
00661   // We assume that <b></b> rectangle takes all the room from "english" left edge to
00662   // "WERBEH" right edge.
00663 
00664   frame = aFirstChild;
00665   for (i = 0; i < aChildCount; i++) {
00666     nsIAtom* frameType = frame->GetType();
00667     if ( (nsLayoutAtoms::inlineFrame == frameType)
00668           || (nsLayoutAtoms::positionedInlineFrame == frameType)
00669           || (nsLayoutAtoms::letterFrame == frameType)
00670           || (nsLayoutAtoms::blockFrame == frameType) ) {
00671       PRInt32 minX = 0x7FFFFFFF;
00672       PRInt32 maxX = 0;
00673       RepositionContainerFrame(aPresContext, frame, minX, maxX);
00674     }
00675     frame = frame->GetNextSibling();
00676   } // for
00677 }
00678 
00679 void
00680 nsBidiPresUtils::RepositionContainerFrame(nsPresContext* aPresContext,
00681                                           nsIFrame* aContainer,
00682                                           PRInt32& aMinX,
00683                                           PRInt32& aMaxX) const
00684 {
00685   nsIFrame* frame;
00686   PRInt32 minX = 0x7FFFFFFF;
00687   PRInt32 maxX = 0;
00688 
00689   nsIFrame* firstChild = aContainer->GetFirstChild(nsnull);
00690 
00691   for (frame = firstChild; frame; frame = frame->GetNextSibling()) {
00692     nsIAtom* frameType = frame->GetType();
00693     if ( (nsLayoutAtoms::inlineFrame == frameType)
00694         || (nsLayoutAtoms::positionedInlineFrame == frameType)
00695         || (nsLayoutAtoms::letterFrame == frameType)
00696         || (nsLayoutAtoms::blockFrame == frameType) ) {
00697       RepositionContainerFrame(aPresContext, frame, minX, maxX);
00698     }
00699     else {
00700       nsRect rect = frame->GetRect();
00701       minX = PR_MIN(minX, rect.x);
00702       maxX = PR_MAX(maxX, rect.XMost());
00703     }
00704   }
00705 
00706   aMinX = PR_MIN(minX, aMinX);
00707   aMaxX = PR_MAX(maxX, aMaxX);
00708 
00709   if (minX < maxX) {
00710     nsRect rect = aContainer->GetRect();
00711     rect.x = minX;
00712     rect.width = maxX - minX;
00713     aContainer->SetRect(rect);
00714   }
00715 
00716   // Now adjust all the kids (kid's coordinates are relative to the parent's)
00717   for (frame = firstChild; frame; frame = frame->GetNextSibling()) {
00718     frame->SetPosition(frame->GetPosition() - nsPoint(minX, 0));
00719   }
00720 }
00721 
00722 PRBool
00723 nsBidiPresUtils::EnsureBidiContinuation(nsPresContext* aPresContext,
00724                                         nsIContent*     aContent,
00725                                         nsIFrame*       aFrame,
00726                                         nsIFrame**      aNewFrame,
00727                                         PRInt32&        aFrameIndex)
00728 {
00729   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00730   if (!aNewFrame) {
00731     return PR_FALSE;
00732   }
00733   *aNewFrame = nsnull;
00734 
00735   if (!aFrame) {
00736     return PR_FALSE;
00737   }
00738   if (aFrameIndex + 1 < mLogicalFrames.Count() ) {
00739     nsIFrame* frame = (nsIFrame*)mLogicalFrames[aFrameIndex + 1];
00740     if (frame->GetContent() == aContent) {
00741       *aNewFrame = frame;
00742       ++aFrameIndex;
00743       aFrame->SetNextInFlow(nsnull);
00744       frame->SetPrevInFlow(nsnull);
00745     }
00746   }
00747   if (nsnull == *aNewFrame) {
00748     mSuccess = CreateBidiContinuation(aPresContext, aContent, aFrame, aNewFrame);
00749     if (NS_FAILED(mSuccess) ) {
00750       return PR_FALSE;
00751     }
00752   }
00753   aPresContext->PropertyTable()->SetProperty(aFrame, nsLayoutAtoms::nextBidi,
00754                                              (void*) *aNewFrame,
00755                                              nsnull, nsnull);
00756   return PR_TRUE;
00757 }
00758 
00759 void
00760 nsBidiPresUtils::RemoveBidiContinuation(nsPresContext* aPresContext,
00761                                         nsIFrame*       aFrame,
00762                                         PRInt32         aFirstIndex,
00763                                         PRInt32         aLastIndex,
00764                                         PRInt32&        aOffset) const
00765 {
00766   nsIFrame*         frame;
00767   PRInt32           index;
00768   nsIFrame*         parent = aFrame->GetParent();
00769       
00770   aOffset = 0;
00771 
00772   for (index = aLastIndex; index > aFirstIndex; index--) {
00773     frame = (nsIFrame*) mLogicalFrames[index];
00774     if (nsLayoutAtoms::directionalFrame == frame->GetType()) {
00775       delete frame;
00776       ++aOffset;
00777     }
00778     else {
00779       if (frame->GetStateBits() & NS_FRAME_IS_BIDI) {
00780         // only delete Bidi frames
00781         if (parent) {
00782           parent->RemoveFrame(nsLayoutAtoms::nextBidi, frame);
00783         }
00784         else {
00785           frame->Destroy(aPresContext);
00786         }
00787       }
00788     }
00789   }
00790   nsIFrame* thisFramesNextBidiFrame;
00791   nsIFrame* previousFramesNextBidiFrame;
00792 
00793   nsPropertyTable *propTable = aPresContext->PropertyTable();
00794   thisFramesNextBidiFrame = NS_STATIC_CAST(nsIFrame*,
00795                       propTable->GetProperty(aFrame, nsLayoutAtoms::nextBidi));
00796 
00797   if (thisFramesNextBidiFrame) {
00798     // Remove nextBidi property, associated with the current frame
00799     // and with all of its prev-in-flow.
00800     frame = aFrame;
00801     do {
00802       propTable->DeleteProperty(frame, nsLayoutAtoms::nextBidi);
00803       frame = frame->GetPrevInFlow();
00804       if (!frame) {
00805         break;
00806       }
00807       previousFramesNextBidiFrame =
00808         NS_STATIC_CAST(nsIFrame*, propTable->GetProperty(frame,
00809                                                     nsLayoutAtoms::nextBidi));
00810     } while (thisFramesNextBidiFrame == previousFramesNextBidiFrame);
00811   } // if (thisFramesNextBidiFrame)
00812 }
00813 
00814 nsresult
00815 nsBidiPresUtils::FormatUnicodeText(nsPresContext*  aPresContext,
00816                                    PRUnichar*       aText,
00817                                    PRInt32&         aTextLength,
00818                                    nsCharType       aCharType,
00819                                    PRBool           aIsOddLevel,
00820                                    PRBool           aIsBidiSystem)
00821 {
00822   NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
00823   nsresult rv = NS_OK;
00824   // ahmed 
00825   //adjusted for correct numeral shaping  
00826   PRUint32 bidiOptions = aPresContext->GetBidi();
00827   switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
00828 
00829     case IBMBIDI_NUMERAL_HINDI:
00830       HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
00831       break;
00832 
00833     case IBMBIDI_NUMERAL_ARABIC:
00834       HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
00835       break;
00836 
00837     case IBMBIDI_NUMERAL_REGULAR:
00838 
00839       switch (aCharType) {
00840 
00841         case eCharType_EuropeanNumber:
00842           HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
00843           break;
00844 
00845         case eCharType_ArabicNumber:
00846           HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
00847           break;
00848 
00849         default:
00850           break;
00851       }
00852       break;
00853       
00854     case IBMBIDI_NUMERAL_HINDICONTEXT:
00855       if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
00856         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
00857       else if (eCharType_EuropeanNumber == aCharType)
00858         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
00859       break;
00860 
00861     case IBMBIDI_NUMERAL_NOMINAL:
00862     default:
00863       break;
00864   }
00865 
00866   PRBool doReverse = PR_FALSE;
00867   PRBool doShape = PR_FALSE;
00868 
00869   if (aIsBidiSystem) {
00870     if ( (CHARTYPE_IS_RTL(aCharType)) ^ (aIsOddLevel) )
00871       doReverse = PR_TRUE;
00872   }
00873   else {
00874     if (aIsOddLevel)
00875       doReverse = PR_TRUE;
00876     if (eCharType_RightToLeftArabic == aCharType) 
00877       doShape = PR_TRUE;
00878   }
00879 
00880   if (doReverse || doShape) {
00881     PRInt32    newLen;
00882 
00883     if (mBuffer.Length() < aTextLength) {
00884       mBuffer.SetLength(aTextLength);
00885       if (mBuffer.Length() < aTextLength)
00886         return NS_ERROR_OUT_OF_MEMORY;
00887     }
00888     PRUnichar* buffer = mBuffer.BeginWriting();
00889 
00890     if (doReverse) {
00891       rv = mBidiEngine->WriteReverse(aText, aTextLength, buffer,
00892                                      NSBIDI_DO_MIRRORING, &newLen);
00893       if (NS_SUCCEEDED(rv) ) {
00894         aTextLength = newLen;
00895         memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
00896       }
00897     }
00898     if (doShape) {
00899       rv = ArabicShaping(aText, aTextLength, buffer, (PRUint32 *)&newLen,
00900                          PR_FALSE, PR_FALSE);
00901       if (NS_SUCCEEDED(rv) ) {
00902         aTextLength = newLen;
00903         memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
00904       }
00905     }
00906   }
00907   StripBidiControlCharacters(aText, aTextLength);
00908   return rv;
00909 }
00910 
00911 void
00912 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar* aText,
00913                                             PRInt32&   aTextLength) const
00914 {
00915   if ( (nsnull == aText) || (aTextLength < 1) ) {
00916     return;
00917   }
00918 
00919   PRInt32 stripLen = 0;
00920 
00921   for (PRInt32 i = 0; i < aTextLength; i++) {
00922     // XXX: This silently ignores surrogate characters.
00923     //      As of Unicode 4.0, all Bidi control characters are within the BMP.
00924     if (mBidiEngine->IsBidiControl((PRUint32)aText[i])) {
00925       ++stripLen;
00926     }
00927     else {
00928       aText[i - stripLen] = aText[i];
00929     }
00930   }
00931   aTextLength -= stripLen;
00932 }
00933  
00934 #if 0 // XXX: for the future use ???
00935 void
00936 RemoveDiacritics(PRUnichar* aText,
00937                  PRInt32&   aTextLength)
00938 {
00939   if (aText && (aTextLength > 0) ) {
00940     PRInt32 offset = 0;
00941 
00942     for (PRInt32 i = 0; i < aTextLength && aText[i]; i++) {
00943       if (IS_BIDI_DIACRITIC(aText[i]) ) {
00944         ++offset;
00945         continue;
00946       }
00947       aText[i - offset] = aText[i];
00948     }
00949     aTextLength = i - offset;
00950     aText[aTextLength] = 0;
00951   }
00952 }
00953 #endif
00954 
00955 void
00956 nsBidiPresUtils::CalculateCharType(PRInt32& aOffset,
00957                                    PRInt32  aCharTypeLimit,
00958                                    PRInt32& aRunLimit,
00959                                    PRInt32& aRunLength,
00960                                    PRInt32& aRunCount,
00961                                    PRUint8& aCharType,
00962                                    PRUint8& aPrevCharType) const
00963 
00964 {
00965   PRBool     strongTypeFound = PR_FALSE;
00966   PRInt32    offset;
00967   nsCharType charType;
00968 
00969   aCharType = eCharType_OtherNeutral;
00970 
00971   for (offset = aOffset; offset < aCharTypeLimit; offset++) {
00972     // Make sure we give RTL chartype to all characters that would be classified
00973     // as Right-To-Left by a bidi platform.
00974     // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
00975     if (IS_HEBREW_CHAR(mBuffer[offset]) ) {
00976       charType = eCharType_RightToLeft;
00977     }
00978     else if (IS_ARABIC_ALPHABETIC(mBuffer[offset]) ) {
00979       charType = eCharType_RightToLeftArabic;
00980     }
00981     else {
00982       mBidiEngine->GetCharTypeAt(offset, &charType);
00983     }
00984 
00985     if (!CHARTYPE_IS_WEAK(charType) ) {
00986 
00987       if (strongTypeFound
00988           && (charType != aPrevCharType)
00989           && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
00990         // Stop at this point to ensure uni-directionality of the text
00991         // (from platform's point of view).
00992         // Also, don't mix Arabic and Hebrew content (since platform may
00993         // provide BIDI support to one of them only).
00994         aRunLength = offset - aOffset;
00995         aRunLimit = offset;
00996         ++aRunCount;
00997         break;
00998       }
00999 
01000       if ( (eCharType_RightToLeftArabic == aPrevCharType
01001             || eCharType_ArabicNumber == aPrevCharType)
01002           && eCharType_EuropeanNumber == charType) {
01003         charType = eCharType_ArabicNumber;
01004       }
01005 
01006       // Set PrevCharType to the last strong type in this frame
01007       // (for correct numeric shaping)
01008       aPrevCharType = charType;
01009 
01010       strongTypeFound = PR_TRUE;
01011       aCharType = charType;
01012     }
01013   }
01014   aOffset = offset;
01015 }
01016 
01017 nsresult nsBidiPresUtils::GetBidiEngine(nsBidi** aBidiEngine)
01018 {
01019   nsresult rv = NS_ERROR_FAILURE;
01020   if (mBidiEngine) {
01021     *aBidiEngine = mBidiEngine;
01022     rv = NS_OK;
01023   }
01024   return rv; 
01025 }
01026 
01027 nsresult nsBidiPresUtils::RenderText(const PRUnichar*     aText,
01028                                      PRInt32              aLength,
01029                                      nsBidiDirection      aBaseDirection,
01030                                      nsPresContext*      aPresContext,
01031                                      nsIRenderingContext& aRenderingContext,
01032                                      nscoord              aX,
01033                                      nscoord              aY,
01034                                      nsBidiPositionResolve* aPosResolve,
01035                                      PRInt32              aPosResolveCount)
01036 {
01037   NS_ASSERTION((aPosResolve == nsnull) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
01038 
01039   PRInt32 runCount;
01040 
01041   mBuffer.Assign(aText, aLength);
01042 
01043   nsresult rv = mBidiEngine->SetPara(mBuffer.get(), aLength, aBaseDirection, nsnull);
01044   if (NS_FAILED(rv))
01045     return rv;
01046 
01047   rv = mBidiEngine->CountRuns(&runCount);
01048   if (NS_FAILED(rv))
01049     return rv;
01050 
01051   nscoord width, xEndRun, xStartText = aX;
01052   PRBool isRTL = PR_FALSE;
01053   PRInt32 i, start, limit, length;
01054   PRUint32 visualStart = 0;
01055   PRUint8 charType;
01056   PRUint8 prevType = eCharType_LeftToRight;
01057   nsBidiLevel level;
01058 
01059   PRUint32 hints = 0;
01060   aRenderingContext.GetHints(hints);
01061   PRBool isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING);
01062       
01063   for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
01064   {
01065     aPosResolve[nPosResolve].visualIndex = kNotFound;
01066     aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
01067   }
01068 
01069   for (i = 0; i < runCount; i++) {
01070     rv = mBidiEngine->GetVisualRun(i, &start, &length, &aBaseDirection);
01071     if (NS_FAILED(rv))
01072       return rv;
01073 
01074     rv = mBidiEngine->GetLogicalRun(start, &limit, &level);
01075     if (NS_FAILED(rv))
01076       return rv;
01077 
01078     PRInt32 subRunLength = limit - start;
01079     PRInt32 lineOffset = start;
01080     PRInt32 typeLimit = PR_MIN(limit, aLength);
01081     PRInt32 subRunCount = 1;
01082     PRInt32 subRunLimit = typeLimit;
01083 
01084     /*
01085      * If |level| is even, i.e. the direction of the run is left-to-right, we
01086      * render the subruns from left to right and increment the x-coordinate
01087      * |aX| by the width of each subrun after rendering.
01088      *
01089      * If |level| is odd, i.e. the direction of the run is right-to-left, we
01090      * render the subruns from right to left. We begin by incrementing |aX| by
01091      * the width of the whole run, and then decrement it by the width of each
01092      * subrun before rendering. After rendering all the subruns, we restore the
01093      * x-coordinate of the end of the run for the start of the next run.
01094      */
01095     if (level & 1) {
01096       aRenderingContext.GetWidth(aText + start, subRunLength, width, nsnull);
01097       aX += width;
01098       xEndRun = aX;
01099     }
01100 
01101     while (subRunCount > 0) {
01102       // CalculateCharType can increment subRunCount if the run
01103       // contains mixed character types
01104       CalculateCharType(lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
01105 
01106       if (eCharType_RightToLeftArabic == charType) {
01107         isBidiSystem = (hints & NS_RENDERING_HINT_ARABIC_SHAPING);
01108       }
01109       if (isBidiSystem && (CHARTYPE_IS_RTL(charType) ^ isRTL) ) {
01110         // set reading order into DC
01111         isRTL = !isRTL;
01112         aRenderingContext.SetRightToLeftText(isRTL);
01113       }
01114       
01115       nsAutoString runVisualText;
01116       runVisualText.Assign(aText + start, subRunLength);
01117       if (runVisualText.Length() < subRunLength)
01118         return NS_ERROR_OUT_OF_MEMORY;
01119       FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
01120                         (nsCharType)charType, level & 1,
01121                         isBidiSystem);
01122 
01123       aRenderingContext.GetWidth(runVisualText.get(), subRunLength, width, nsnull);
01124       if (level & 1) {
01125         aX -= width;
01126       }
01127       aRenderingContext.DrawString(runVisualText.get(), subRunLength, aX, aY, width);
01128 
01129       /*
01130        * The caller may request to calculate the visual position of one
01131        * or more characters.
01132        */
01133       for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
01134       {
01135         nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
01136         /*
01137          * Did we already resolve this position's visual metric? If so, skip.
01138          */
01139         if (posResolve->visualLeftTwips != kNotFound)
01140            continue;
01141            
01142         /*
01143          * First find out if the logical position is within this run.
01144          */
01145         if (start <= posResolve->logicalIndex &&
01146             start + subRunLength > posResolve->logicalIndex) {
01147           /*
01148            * If this run is only one character long, we have an easy case:
01149            * the visual position is the x-coord of the start of the run
01150            * less the x-coord of the start of the whole text (saved in xStartText).
01151            */
01152           if (subRunLength == 1) {
01153             posResolve->visualIndex = visualStart;
01154             posResolve->visualLeftTwips = aX - xStartText;
01155           }
01156           /*
01157            * Otherwise, we need to measure the width of the run's part
01158            * which is to the visual left of the index.
01159            * In other words, the run is broken in two, around the logical index,
01160            * and we measure the part which is visually left.
01161            * If the run is right-to-left, this part will span from after the index
01162            * up to the end of the run; if it is left-to-right, this part will span
01163            * from the start of the run up to (and inclduing) the character before the index.
01164            */
01165           else {
01166             nscoord subWidth;
01167             // The position in the text where this run's "left part" begins.
01168             const PRUnichar* visualLeftPart;
01169             if (level & 1) {
01170               // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
01171               posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
01172               // Skipping to the "left part".
01173               visualLeftPart = aText + posResolve->logicalIndex + 1;
01174             }
01175             else {
01176               posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
01177               // Skipping to the "left part".
01178               visualLeftPart = aText + start;
01179             }
01180             // The delta between the start of the run and the left part's end.
01181             PRInt32 visualLeftLength = posResolve->visualIndex - visualStart;
01182             aRenderingContext.GetWidth(visualLeftPart,
01183                                        visualLeftLength,
01184                                        subWidth, nsnull);
01185             posResolve->visualLeftTwips = aX + subWidth - xStartText;
01186           }
01187         }
01188       }
01189 
01190       if (!(level & 1)) {
01191         aX += width;
01192       }
01193 
01194       --subRunCount;
01195       start = lineOffset;
01196       subRunLimit = typeLimit;
01197       subRunLength = typeLimit - lineOffset;
01198     } // while
01199     if (level & 1) {
01200       aX = xEndRun;
01201     }
01202     
01203     visualStart += length;
01204   } // for
01205 
01206   // Restore original reading order
01207   if (isRTL) {
01208     aRenderingContext.SetRightToLeftText(PR_FALSE);
01209   }
01210   return NS_OK;
01211 }
01212   
01213 nsresult
01214 nsBidiPresUtils::ReorderUnicodeText(PRUnichar*       aText,
01215                                     PRInt32&         aTextLength,
01216                                     nsCharType       aCharType,
01217                                     PRBool           aIsOddLevel,
01218                                     PRBool           aIsBidiSystem)
01219 {
01220   NS_ASSERTION(aIsOddLevel == 0 || aIsOddLevel == 1, "aIsOddLevel should be 0 or 1");
01221   nsresult rv = NS_OK;
01222   PRBool doReverse = PR_FALSE;
01223 
01224   if (aIsBidiSystem) {
01225     if ( (CHARTYPE_IS_RTL(aCharType)) ^ (aIsOddLevel) )
01226       doReverse = PR_TRUE;
01227   }
01228   else {
01229     if (aIsOddLevel)
01230       doReverse = PR_TRUE;
01231   }
01232 
01233   if (doReverse) {
01234     PRInt32    newLen;
01235 
01236     if (mBuffer.Length() < aTextLength) {
01237       mBuffer.SetLength(aTextLength);
01238       if (mBuffer.Length() < aTextLength)
01239         return NS_ERROR_OUT_OF_MEMORY;
01240     }
01241     PRUnichar* buffer = mBuffer.BeginWriting();
01242 
01243     if (doReverse) {
01244       rv = mBidiEngine->WriteReverse(aText, aTextLength, buffer,
01245                                      NSBIDI_DO_MIRRORING, &newLen);
01246       if (NS_SUCCEEDED(rv) ) {
01247         aTextLength = newLen;
01248         memcpy(aText, buffer, aTextLength * sizeof(PRUnichar) );
01249       }
01250     }
01251   }
01252   return rv;
01253 }
01254 
01255 #endif // IBMBIDI