1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Uri Bernstein <uriber@gmail.com>
24 * Haamed Gheibi <gheibi@metanetworking.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
42 #include "nsBidiPresUtils.h"
43 #include "nsTextFragment.h"
44 #include "nsGkAtoms.h"
45 #include "nsPresContext.h"
46 #include "nsIRenderingContext.h"
47 #include "nsIServiceManager.h"
48 #include "nsFrameManager.h"
49 #include "nsBidiFrames.h"
50 #include "nsBidiUtils.h"
51 #include "nsCSSFrameConstructor.h"
52 #include "nsHTMLContainerFrame.h"
53 #include "nsInlineFrame.h"
54 #include "nsPlaceholderFrame.h"
56 static NS_DEFINE_IID(kInlineFrameCID
, NS_INLINE_FRAME_CID
);
58 static const PRUnichar kSpace
= 0x0020;
59 static const PRUnichar kLineSeparator
= 0x2028;
60 static const PRUnichar kObjectSubstitute
= 0xFFFC;
61 static const PRUnichar kLRE
= 0x202A;
62 static const PRUnichar kRLE
= 0x202B;
63 static const PRUnichar kLRO
= 0x202D;
64 static const PRUnichar kRLO
= 0x202E;
65 static const PRUnichar kPDF
= 0x202C;
66 static const PRUnichar ALEF
= 0x05D0;
68 #define CHAR_IS_HEBREW(c) ((0x0590 <= (c)) && ((c)<= 0x05FF))
69 // Note: The above code are moved from gfx/src/windows/nsRenderingContextWin.cpp
72 NS_NewDirectionalFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRUnichar aChar
);
74 nsBidiPresUtils::nsBidiPresUtils() : mArraySize(8),
77 mSuccess(NS_ERROR_FAILURE
),
80 mBidiEngine
= new nsBidi();
81 if (mBidiEngine
&& mContentToFrameIndex
.Init()) {
86 nsBidiPresUtils::~nsBidiPresUtils()
98 nsBidiPresUtils::IsSuccessful() const
100 return NS_SUCCEEDED(mSuccess
);
103 /* Some helper methods for Resolve() */
105 // Should this frame be split between text runs?
107 IsBidiSplittable(nsIFrame
* aFrame
) {
108 nsIAtom
* frameType
= aFrame
->GetType();
109 // Bidi inline containers should be split, unless they're line frames.
110 return aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)
111 && frameType
!= nsGkAtoms::lineFrame
;
115 SplitInlineAncestors(nsIFrame
* aFrame
)
117 nsPresContext
*presContext
= aFrame
->PresContext();
118 nsIPresShell
*presShell
= presContext
->PresShell();
119 nsIFrame
* frame
= aFrame
;
120 nsIFrame
* parent
= aFrame
->GetParent();
121 nsIFrame
* newFrame
= aFrame
->GetNextSibling();
124 while (IsBidiSplittable(parent
)) {
125 nsIFrame
* grandparent
= parent
->GetParent();
126 NS_ASSERTION(grandparent
, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
128 nsresult rv
= presShell
->FrameConstructor()->
129 CreateContinuingFrame(presContext
, parent
, grandparent
, &newParent
, PR_FALSE
);
134 // The new parent adopts the new frame
135 frame
->SetNextSibling(nsnull
);
136 rv
= newParent
->InsertFrames(nsGkAtoms::nextBidi
, nsnull
, newFrame
);
141 // Reparent views as necessary
142 rv
= nsHTMLContainerFrame::ReparentFrameViewList(presContext
, newFrame
, parent
, newParent
);
147 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
148 rv
= grandparent
->InsertFrames(nsGkAtoms::nextBidi
, parent
, newParent
);
154 newFrame
= newParent
;
155 parent
= grandparent
;
161 // Convert bidi continuations to fluid continuations for a frame and all of its
164 JoinInlineAncestors(nsIFrame
* aFrame
)
166 nsIFrame
* frame
= aFrame
;
167 while (frame
&& IsBidiSplittable(frame
)) {
168 nsIFrame
* next
= frame
->GetNextContinuation();
170 NS_ASSERTION (!frame
->GetNextInFlow() || frame
->GetNextInFlow() == next
,
171 "next-in-flow is not next continuation!");
172 frame
->SetNextInFlow(next
);
174 NS_ASSERTION (!next
->GetPrevInFlow() || next
->GetPrevInFlow() == frame
,
175 "prev-in-flow is not prev continuation!");
176 next
->SetPrevInFlow(frame
);
178 // Join the parent only as long as we're its last child.
179 if (frame
->GetNextSibling())
181 frame
= frame
->GetParent();
186 CreateBidiContinuation(nsIFrame
* aFrame
,
187 nsIFrame
** aNewFrame
)
189 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
190 NS_PRECONDITION(aFrame
, "null ptr");
194 nsPresContext
*presContext
= aFrame
->PresContext();
195 nsIPresShell
*presShell
= presContext
->PresShell();
196 NS_ASSERTION(presShell
, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
198 nsIFrame
* parent
= aFrame
->GetParent();
199 NS_ASSERTION(parent
, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
201 nsresult rv
= presShell
->FrameConstructor()->
202 CreateContinuingFrame(presContext
, aFrame
, parent
, aNewFrame
, PR_FALSE
);
207 // The list name nsGkAtoms::nextBidi would indicate we don't want reflow
208 rv
= parent
->InsertFrames(nsGkAtoms::nextBidi
, aFrame
, *aNewFrame
);
213 // Split inline ancestor frames
214 rv
= SplitInlineAncestors(aFrame
);
223 IsFrameInCurrentLine(nsBlockInFlowLineIterator
* aLineIter
,
224 nsIFrame
* aPrevFrame
, nsIFrame
* aFrame
)
226 nsIFrame
* endFrame
= aLineIter
->IsLastLineInList() ? nsnull
:
227 aLineIter
->GetLine().next()->mFirstChild
;
228 nsIFrame
* startFrame
= aPrevFrame
? aPrevFrame
: aLineIter
->GetLine()->mFirstChild
;
229 return nsFrameList(startFrame
).ContainsFrameBefore(aFrame
, endFrame
);
233 AdvanceLineIteratorToFrame(nsIFrame
* aFrame
,
234 nsBlockInFlowLineIterator
* aLineIter
,
235 nsIFrame
*& aPrevFrame
)
237 // Advance aLine to the line containing aFrame
238 nsIFrame
* child
= aFrame
;
239 nsFrameManager
* frameManager
= aFrame
->PresContext()->FrameManager();
240 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
241 while (parent
&& !nsLayoutUtils::GetAsBlock(parent
)) {
243 parent
= nsLayoutUtils::GetParentOrPlaceholderFor(frameManager
, child
);
245 NS_ASSERTION (parent
, "aFrame is not a descendent of aBlockFrame");
246 while (!IsFrameInCurrentLine(aLineIter
, aPrevFrame
, child
)) {
251 NS_ASSERTION(hasNext
, "Can't find frame in lines!");
258 * Overview of the implementation of Resolve():
260 * Walk through the descendants of aBlockFrame and build:
261 * * mLogicalArray: an nsVoidArray of nsIFrame* pointers in logical order
262 * * mBuffer: an nsAutoString containing a representation of
263 * the content of the frames.
264 * In the case of text frames, this is the actual text context of the
265 * frames, but some other elements are represented in a symbolic form which
266 * will make the Unicode Bidi Algorithm give the correct results.
267 * Bidi embeddings and overrides set by CSS or <bdo> elements are
268 * represented by the corresponding Unicode control characters.
269 * <br> elements are represented by U+2028 LINE SEPARATOR
270 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
273 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
274 * by nsBidi::SetPara() and division into directional runs by
275 * nsBidi::CountRuns().
277 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
278 * correlate them with the frames indexed in mLogicalArray, setting the
279 * baseLevel, embeddingLevel, and charType properties according to the results
280 * returned by the Bidi engine and CalculateCharType().
282 * The rendering layer requires each text frame to contain text in only one
283 * direction and of only one character type, so we may need to call
284 * EnsureBidiContinuation() to split frames. We may also need to call
285 * RemoveBidiContinuation() to convert frames created by
286 * EnsureBidiContinuation() in previous reflows into fluid continuations.
289 nsBidiPresUtils::Resolve(nsBlockFrame
* aBlockFrame
,
290 PRBool aIsVisualFormControl
)
292 mLogicalFrames
.Clear();
293 mContentToFrameIndex
.Clear();
295 nsPresContext
*presContext
= aBlockFrame
->PresContext();
296 nsIPresShell
* shell
= presContext
->PresShell();
297 nsStyleContext
* styleContext
= aBlockFrame
->GetStyleContext();
299 // handle bidi-override being set on the block itself before calling
301 const nsStyleVisibility
* vis
= aBlockFrame
->GetStyleVisibility();
302 const nsStyleTextReset
* text
= aBlockFrame
->GetStyleTextReset();
304 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
305 nsIFrame
*directionalFrame
= nsnull
;
307 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
308 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kRLO
);
310 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
311 directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kLRO
);
314 if (directionalFrame
) {
315 mLogicalFrames
.AppendElement(directionalFrame
);
318 for (nsBlockFrame
* block
= aBlockFrame
; block
;
319 block
= static_cast<nsBlockFrame
*>(block
->GetNextContinuation())) {
320 block
->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
321 InitLogicalArray(block
->GetFirstChild(nsnull
));
324 if (text
->mUnicodeBidi
== NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
325 nsIFrame
* directionalFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
326 if (directionalFrame
) {
327 mLogicalFrames
.AppendElement(directionalFrame
);
333 PRInt32 bufferLength
= mBuffer
.Length();
335 if (bufferLength
< 1) {
340 PRUint8 embeddingLevel
;
342 nsBidiLevel paraLevel
= embeddingLevel
=
343 (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
)
344 ? NSBIDI_RTL
: NSBIDI_LTR
;
346 mSuccess
= mBidiEngine
->SetPara(mBuffer
.get(), bufferLength
, paraLevel
, nsnull
);
347 if (NS_FAILED(mSuccess
) ) {
352 if (aIsVisualFormControl
) {
355 isVisual
= presContext
->IsVisualMode();
357 mSuccess
= mBidiEngine
->CountRuns(&runCount
);
358 if (NS_FAILED(mSuccess
) ) {
361 PRInt32 runLength
= 0;
362 PRInt32 fragmentLength
= 0;
364 PRInt32 frameIndex
= -1;
365 PRInt32 frameCount
= mLogicalFrames
.Count();
366 PRInt32 contentOffset
= 0; // offset within current frame
367 PRInt32 lineOffset
= 0; // offset within mBuffer
368 PRInt32 logicalLimit
= 0;
371 PRUint8 prevType
= eCharType_LeftToRight
;
372 PRBool isTextFrame
= PR_FALSE
;
373 nsIFrame
* frame
= nsnull
;
375 nsIContent
* content
= nsnull
;
376 const nsTextFragment
* fragment
;
377 nsIAtom
* frameType
= nsnull
;
379 nsPropertyTable
*propTable
= presContext
->PropertyTable();
381 nsBlockInFlowLineIterator
lineIter(aBlockFrame
, aBlockFrame
->begin_lines(), PR_FALSE
);
382 if (lineIter
.GetLine() == aBlockFrame
->end_lines()) {
383 // Advance to first valid line (might be in a next-continuation)
386 nsIFrame
* prevFrame
= nsnull
;
387 PRBool lineNeedsUpdate
= PR_FALSE
;
390 if (fragmentLength
<= 0) {
391 if (++frameIndex
>= frameCount
) {
396 frame
= (nsIFrame
*) (mLogicalFrames
[frameIndex
]);
397 frameType
= frame
->GetType();
398 lineNeedsUpdate
= PR_TRUE
;
399 if (nsGkAtoms::textFrame
== frameType
) {
400 content
= frame
->GetContent();
405 fragment
= content
->GetText();
407 mSuccess
= NS_ERROR_FAILURE
;
410 fragmentLength
= fragment
->GetLength();
411 isTextFrame
= PR_TRUE
;
414 isTextFrame
= PR_FALSE
;
417 } // if (fragmentLength <= 0)
418 if (runLength
<= 0) {
419 if (++numRun
>= runCount
) {
422 lineOffset
= logicalLimit
;
423 if (NS_FAILED(mBidiEngine
->GetLogicalRun(
424 lineOffset
, &logicalLimit
, &embeddingLevel
) ) ) {
427 runLength
= logicalLimit
- lineOffset
;
429 embeddingLevel
= paraLevel
;
431 } // if (runLength <= 0)
433 if (nsGkAtoms::directionalFrame
== frameType
) {
439 propTable
->SetProperty(frame
, nsGkAtoms::embeddingLevel
,
440 NS_INT32_TO_PTR(embeddingLevel
), nsnull
, nsnull
);
441 propTable
->SetProperty(frame
, nsGkAtoms::baseLevel
,
442 NS_INT32_TO_PTR(paraLevel
), nsnull
, nsnull
);
444 PRInt32 typeLimit
= PR_MIN(logicalLimit
, lineOffset
+ fragmentLength
);
445 CalculateCharType(lineOffset
, typeLimit
, logicalLimit
, runLength
,
446 runCount
, charType
, prevType
);
447 // IBMBIDI - Egypt - Start
448 propTable
->SetProperty(frame
, nsGkAtoms::charType
,
449 NS_INT32_TO_PTR(charType
), nsnull
, nsnull
);
450 // IBMBIDI - Egypt - End
452 if ( (runLength
> 0) && (runLength
< fragmentLength
) ) {
453 if (lineNeedsUpdate
) {
454 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
455 lineNeedsUpdate
= PR_FALSE
;
457 lineIter
.GetLine()->MarkDirty();
458 if (!EnsureBidiContinuation(frame
, &nextBidi
, frameIndex
,
460 contentOffset
+ runLength
,
465 contentOffset
+= runLength
;
466 } // if (runLength < fragmentLength)
468 PRInt32 newIndex
= 0;
469 mContentToFrameIndex
.Get(content
, &newIndex
);
470 if (newIndex
> frameIndex
) {
471 RemoveBidiContinuation(frame
, frameIndex
, newIndex
, temp
);
472 if (lineNeedsUpdate
) {
473 AdvanceLineIteratorToFrame(frame
, &lineIter
, prevFrame
);
474 lineNeedsUpdate
= PR_FALSE
;
476 lineIter
.GetLine()->MarkDirty();
478 fragmentLength
-= temp
;
480 frameIndex
= newIndex
;
482 frame
->AdjustOffsetsForBidi(contentOffset
, contentOffset
+ fragmentLength
);
488 } // not directionalFrame
490 runLength
-= fragmentLength
;
491 fragmentLength
-= temp
;
493 if (frame
&& fragmentLength
<= 0) {
494 if (runLength
<= 0) {
495 // If the frame is at the end of a run, split all ancestor inlines that need splitting.
496 nsIFrame
* child
= frame
;
497 nsIFrame
* parent
= frame
->GetParent();
498 // As long as we're on the last sibling, the parent doesn't have to be split.
500 IsBidiSplittable(parent
) &&
501 !child
->GetNextSibling()) {
503 parent
= child
->GetParent();
505 if (parent
&& IsBidiSplittable(parent
))
506 SplitInlineAncestors(child
);
508 else if (!frame
->GetNextSibling()) {
509 // We're not at an end of a run, and |frame| is the last child of its parent.
510 // If its ancestors happen to have bidi continuations, convert them into
511 // fluid continuations.
512 nsIFrame
* parent
= frame
->GetParent();
513 JoinInlineAncestors(parent
);
520 // Should this frame be treated as a leaf (e.g. when building mLogicalArray)?
521 PRBool
IsBidiLeaf(nsIFrame
* aFrame
) {
522 nsIFrame
* kid
= aFrame
->GetFirstChild(nsnull
);
524 || !aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
);
528 nsBidiPresUtils::InitLogicalArray(nsIFrame
* aCurrentFrame
)
533 nsIPresShell
* shell
= aCurrentFrame
->PresContext()->PresShell();
534 nsStyleContext
* styleContext
;
536 for (nsIFrame
* childFrame
= aCurrentFrame
; childFrame
;
537 childFrame
= childFrame
->GetNextSibling()) {
539 // If the real frame for a placeholder is an inline container, we need to
540 // drill down into it and include its contents in Bidi resolution. If it
541 // isn't an inline container, we just use the placeholder.
542 nsIFrame
* frame
= childFrame
;
543 if (nsGkAtoms::placeholderFrame
== childFrame
->GetType()) {
544 nsIFrame
* realFrame
=
545 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame
);
546 if (realFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)) {
552 if (frame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)) {
553 const nsStyleVisibility
* vis
= frame
->GetStyleVisibility();
554 const nsStyleTextReset
* text
= frame
->GetStyleTextReset();
555 switch (text
->mUnicodeBidi
) {
556 case NS_STYLE_UNICODE_BIDI_NORMAL
:
558 case NS_STYLE_UNICODE_BIDI_EMBED
:
559 styleContext
= frame
->GetStyleContext();
561 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
564 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
568 case NS_STYLE_UNICODE_BIDI_OVERRIDE
:
569 styleContext
= frame
->GetStyleContext();
571 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
574 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
580 // Create a directional frame before the first frame of an
581 // element specifying embedding or override
582 if (ch
!= 0 && !frame
->GetPrevContinuation()) {
583 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, ch
);
585 mLogicalFrames
.AppendElement(dirFrame
);
590 if (IsBidiLeaf(frame
)) {
591 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
592 * and add its index to the mContentToFrameIndex hashtable. This
593 * will be used in RemoveBidiContinuation() to identify the last
594 * frame in the array with a given content.
596 nsIContent
* content
= frame
->GetContent();
598 mContentToFrameIndex
.Put(content
, mLogicalFrames
.Count());
600 mLogicalFrames
.AppendElement(frame
);
603 nsIFrame
* kid
= frame
->GetFirstChild(nsnull
);
604 InitLogicalArray(kid
);
607 // If the element is attributed by dir, indicate direction pop (add PDF frame)
608 if (ch
!= 0 && !frame
->GetNextContinuation()) {
609 // Create a directional frame after the last frame of an
610 // element specifying embedding or override
611 nsIFrame
* dirFrame
= NS_NewDirectionalFrame(shell
, styleContext
, kPDF
);
613 mLogicalFrames
.AppendElement(dirFrame
);
620 nsBidiPresUtils::CreateBlockBuffer()
622 mBuffer
.SetLength(0);
625 nsIContent
* prevContent
= nsnull
;
627 PRUint32 count
= mLogicalFrames
.Count();
629 for (i
= 0; i
< count
; i
++) {
630 frame
= (nsIFrame
*) (mLogicalFrames
[i
]);
631 nsIAtom
* frameType
= frame
->GetType();
633 if (nsGkAtoms::textFrame
== frameType
) {
634 nsIContent
* content
= frame
->GetContent();
639 if (content
== prevContent
) {
642 prevContent
= content
;
643 content
->AppendTextTo(mBuffer
);
645 else if (nsGkAtoms::brFrame
== frameType
) { // break frame
646 // Append line separator
647 mBuffer
.Append(kLineSeparator
);
649 else if (nsGkAtoms::directionalFrame
== frameType
) {
650 nsDirectionalFrame
* dirFrame
= static_cast<nsDirectionalFrame
*>(frame
);
651 mBuffer
.Append(dirFrame
->GetChar());
653 else { // not text frame
654 // See the Unicode Bidi Algorithm:
655 // "...inline objects (such as graphics) are treated as if they are ... U+FFFC"
656 mBuffer
.Append(kObjectSubstitute
);
659 // XXX: TODO: Handle preformatted text ('\n')
660 mBuffer
.ReplaceChar("\t\r\n", kSpace
);
664 nsBidiPresUtils::ReorderFrames(nsIFrame
* aFirstFrameOnLine
,
665 PRInt32 aNumFramesOnLine
)
667 // If this line consists of a line frame, reorder the line frame's children.
668 if (aFirstFrameOnLine
->GetType() == nsGkAtoms::lineFrame
) {
669 aFirstFrameOnLine
= aFirstFrameOnLine
->GetFirstChild(nsnull
);
670 if (!aFirstFrameOnLine
)
672 // All children of the line frame are on the first line. Setting aNumFramesOnLine
673 // to -1 makes InitLogicalArrayFromLine look at all of them.
674 aNumFramesOnLine
= -1;
677 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
681 Reorder(isReordered
, hasRTLFrames
);
682 RepositionInlineFrames(aFirstFrameOnLine
);
686 nsBidiPresUtils::Reorder(PRBool
& aReordered
, PRBool
& aHasRTLFrames
)
688 aReordered
= PR_FALSE
;
689 aHasRTLFrames
= PR_FALSE
;
690 PRInt32 count
= mLogicalFrames
.Count();
692 if (mArraySize
< count
) {
693 mArraySize
= count
<< 1;
704 mLevels
= new PRUint8
[mArraySize
];
706 return NS_ERROR_OUT_OF_MEMORY
;
709 memset(mLevels
, 0, sizeof(PRUint8
) * mArraySize
);
714 for (i
= 0; i
< count
; i
++) {
715 frame
= (nsIFrame
*) (mLogicalFrames
[i
]);
716 mLevels
[i
] = GetFrameEmbeddingLevel(frame
);
717 if (mLevels
[i
] & 1) {
718 aHasRTLFrames
= PR_TRUE
;
722 mIndexMap
= new PRInt32
[mArraySize
];
725 mSuccess
= NS_ERROR_OUT_OF_MEMORY
;
728 memset(mIndexMap
, 0, sizeof(PRUint32
) * mArraySize
);
730 mSuccess
= mBidiEngine
->ReorderVisual(mLevels
, count
, mIndexMap
);
732 if (NS_SUCCEEDED(mSuccess
) ) {
733 mVisualFrames
.Clear();
735 for (i
= 0; i
< count
; i
++) {
736 mVisualFrames
.AppendElement(mLogicalFrames
[mIndexMap
[i
]]);
737 if (i
!= mIndexMap
[i
]) {
738 aReordered
= PR_TRUE
;
741 } // NS_SUCCEEDED(mSuccess)
744 if (NS_FAILED(mSuccess
) ) {
745 aReordered
= PR_FALSE
;
751 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame
* aFrame
)
753 nsIFrame
* firstLeaf
= aFrame
;
754 while (!IsBidiLeaf(firstLeaf
)) {
755 firstLeaf
= firstLeaf
->GetFirstChild(nsnull
);
757 return NS_GET_EMBEDDING_LEVEL(firstLeaf
);
761 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame
* aFrame
)
763 nsIFrame
* firstLeaf
= aFrame
;
764 while (!IsBidiLeaf(firstLeaf
)) {
765 firstLeaf
= firstLeaf
->GetFirstChild(nsnull
);
767 return NS_GET_BASE_LEVEL(firstLeaf
);
771 nsBidiPresUtils::IsLeftOrRightMost(nsIFrame
* aFrame
,
772 nsContinuationStates
* aContinuationStates
,
773 PRBool
& aIsLeftMost
/* out */,
774 PRBool
& aIsRightMost
/* out */) const
776 const nsStyleVisibility
* vis
= aFrame
->GetStyleVisibility();
777 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
780 * Since we lay out frames from left to right (in both LTR and RTL), visiting a
781 * frame with 'mFirstVisualFrame == nsnull', means it's the first appearance of
782 * one of its continuation chain frames on the line.
783 * To determine if it's the last visual frame of its continuation chain on the line
784 * or not, we count the number of frames of the chain on the line, and then reduce
785 * it when we lay out a frame of the chain. If this value becomes 1 it means
786 * that it's the last visual frame of its continuation chain on this line.
789 nsFrameContinuationState
* frameState
= aContinuationStates
->GetEntry(aFrame
);
790 nsFrameContinuationState
* firstFrameState
;
792 if (!frameState
->mFirstVisualFrame
) {
793 // aFrame is the first visual frame of its continuation chain
794 nsFrameContinuationState
* contState
;
797 frameState
->mFrameCount
= 1;
798 frameState
->mFirstVisualFrame
= aFrame
;
801 * Traverse continuation chain of aFrame in both backward and forward
802 * directions while the frames are on this line. Count the frames and
803 * set their mFirstVisualFrame to aFrame.
805 // Traverse continuation chain backward
806 for (frame
= aFrame
->GetPrevContinuation();
807 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
808 frame
= frame
->GetPrevContinuation()) {
809 frameState
->mFrameCount
++;
810 contState
->mFirstVisualFrame
= aFrame
;
812 frameState
->mHasContOnPrevLines
= (frame
!= nsnull
);
814 // Traverse continuation chain forward
815 for (frame
= aFrame
->GetNextContinuation();
816 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
817 frame
= frame
->GetNextContinuation()) {
818 frameState
->mFrameCount
++;
819 contState
->mFirstVisualFrame
= aFrame
;
821 frameState
->mHasContOnNextLines
= (frame
!= nsnull
);
823 aIsLeftMost
= isLTR
? !frameState
->mHasContOnPrevLines
824 : !frameState
->mHasContOnNextLines
;
825 firstFrameState
= frameState
;
827 // aFrame is not the first visual frame of its continuation chain
828 aIsLeftMost
= PR_FALSE
;
829 firstFrameState
= aContinuationStates
->GetEntry(frameState
->mFirstVisualFrame
);
832 aIsRightMost
= (firstFrameState
->mFrameCount
== 1) &&
833 (isLTR
? !firstFrameState
->mHasContOnNextLines
834 : !firstFrameState
->mHasContOnPrevLines
);
836 // Reduce number of remaining frames of the continuation chain on the line.
837 firstFrameState
->mFrameCount
--;
841 nsBidiPresUtils::RepositionFrame(nsIFrame
* aFrame
,
844 nsContinuationStates
* aContinuationStates
) const
849 PRBool isLeftMost
, isRightMost
;
850 IsLeftOrRightMost(aFrame
,
852 isLeftMost
/* out */,
853 isRightMost
/* out */);
856 aFrame
->QueryInterface(kInlineFrameCID
, (void**)&testFrame
);
859 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET
);
862 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
864 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LEFT_MOST
);
867 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
869 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_RIGHT_MOST
);
871 // This method is called from nsBlockFrame::PlaceLine via the call to
872 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
873 // have been reflowed, which is required for GetUsedMargin/Border/Padding
874 nsMargin margin
= aFrame
->GetUsedMargin();
876 aLeft
+= margin
.left
;
878 nscoord start
= aLeft
;
880 if (!IsBidiLeaf(aFrame
))
883 nsMargin borderPadding
= aFrame
->GetUsedBorderAndPadding();
885 x
+= borderPadding
.left
;
888 // If aIsOddLevel is true, so we need to traverse the child list
889 // in reverse order, to make it O(n) we store the list locally and
890 // iterate the list reversely
891 nsVoidArray childList
;
892 nsIFrame
*frame
= aFrame
->GetFirstChild(nsnull
);
893 if (frame
&& aIsOddLevel
) {
894 childList
.AppendElement(nsnull
);
896 childList
.AppendElement(frame
);
897 frame
= frame
->GetNextSibling();
899 frame
= (nsIFrame
*)childList
[childList
.Count() - 1];
902 // Reposition the child frames
905 RepositionFrame(frame
,
908 aContinuationStates
);
910 frame
= aIsOddLevel
?
911 (nsIFrame
*)childList
[childList
.Count() - index
- 1] :
912 frame
->GetNextSibling();
916 x
+= borderPadding
.right
;
920 aLeft
+= aFrame
->GetSize().width
;
922 nsRect rect
= aFrame
->GetRect();
923 aFrame
->SetRect(nsRect(start
, rect
.y
, aLeft
- start
, rect
.height
));
926 aLeft
+= margin
.right
;
930 nsBidiPresUtils::InitContinuationStates(nsIFrame
* aFrame
,
931 nsContinuationStates
* aContinuationStates
) const
933 nsFrameContinuationState
* state
= aContinuationStates
->PutEntry(aFrame
);
934 state
->mFirstVisualFrame
= nsnull
;
935 state
->mFrameCount
= 0;
937 if (!IsBidiLeaf(aFrame
)) {
938 // Continue for child frames
940 for (frame
= aFrame
->GetFirstChild(nsnull
);
942 frame
= frame
->GetNextSibling()) {
943 InitContinuationStates(frame
,
944 aContinuationStates
);
950 nsBidiPresUtils::RepositionInlineFrames(nsIFrame
* aFirstChild
) const
952 const nsStyleVisibility
* vis
= aFirstChild
->GetStyleVisibility();
953 PRBool isLTR
= (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
);
954 nscoord leftSpace
= 0;
956 // This method is called from nsBlockFrame::PlaceLine via the call to
957 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
958 // have been reflowed, which is required for GetUsedMargin/Border/Padding
959 nsMargin margin
= aFirstChild
->GetUsedMargin();
960 if (!aFirstChild
->GetPrevContinuation())
961 leftSpace
= isLTR
? margin
.left
: margin
.right
;
963 nscoord left
= aFirstChild
->GetPosition().x
- leftSpace
;
965 PRInt32 count
= mVisualFrames
.Count();
967 nsContinuationStates continuationStates
;
969 continuationStates
.Init();
971 // Initialize continuation states to (nsnull, 0) for
972 // each frame on the line.
973 for (index
= 0; index
< count
; index
++) {
974 InitContinuationStates((nsIFrame
*)mVisualFrames
[index
],
975 &continuationStates
);
978 // Reposition frames in visual order
979 for (index
= 0; index
< count
; index
++) {
980 frame
= (nsIFrame
*) (mVisualFrames
[index
]);
981 RepositionFrame(frame
,
982 (mLevels
[mIndexMap
[index
]] & 1),
984 &continuationStates
);
989 nsBidiPresUtils::InitLogicalArrayFromLine(nsIFrame
* aFirstFrameOnLine
,
990 PRInt32 aNumFramesOnLine
) {
991 mLogicalFrames
.Clear();
992 for (nsIFrame
* frame
= aFirstFrameOnLine
;
993 frame
&& aNumFramesOnLine
--;
994 frame
= frame
->GetNextSibling()) {
995 mLogicalFrames
.AppendElement(frame
);
1000 nsBidiPresUtils::CheckLineOrder(nsIFrame
* aFirstFrameOnLine
,
1001 PRInt32 aNumFramesOnLine
,
1002 nsIFrame
** aFirstVisual
,
1003 nsIFrame
** aLastVisual
)
1005 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1008 PRBool hasRTLFrames
;
1009 Reorder(isReordered
, hasRTLFrames
);
1010 PRInt32 count
= mLogicalFrames
.Count();
1013 *aFirstVisual
= (nsIFrame
*)mVisualFrames
[0];
1016 *aLastVisual
= (nsIFrame
*)mVisualFrames
[count
-1];
1019 // If there's an RTL frame, assume the line is reordered
1020 return isReordered
|| hasRTLFrames
;
1024 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame
* aFrame
,
1025 nsIFrame
* aFirstFrameOnLine
,
1026 PRInt32 aNumFramesOnLine
)
1028 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1031 PRBool hasRTLFrames
;
1032 Reorder(isReordered
, hasRTLFrames
);
1033 PRInt32 count
= mVisualFrames
.Count();
1035 if (aFrame
== nsnull
)
1036 return (nsIFrame
*)mVisualFrames
[0];
1038 for (PRInt32 i
= 0; i
< count
- 1; i
++) {
1039 if ((nsIFrame
*)mVisualFrames
[i
] == aFrame
) {
1040 return (nsIFrame
*)mVisualFrames
[i
+1];
1048 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame
* aFrame
,
1049 nsIFrame
* aFirstFrameOnLine
,
1050 PRInt32 aNumFramesOnLine
)
1052 InitLogicalArrayFromLine(aFirstFrameOnLine
, aNumFramesOnLine
);
1055 PRBool hasRTLFrames
;
1056 Reorder(isReordered
, hasRTLFrames
);
1057 PRInt32 count
= mVisualFrames
.Count();
1059 if (aFrame
== nsnull
)
1060 return (nsIFrame
*)mVisualFrames
[count
-1];
1062 for (PRInt32 i
= 1; i
< count
; i
++) {
1063 if ((nsIFrame
*)mVisualFrames
[i
] == aFrame
) {
1064 return (nsIFrame
*)mVisualFrames
[i
-1];
1072 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame
* aFrame
,
1073 nsIFrame
** aNewFrame
,
1074 PRInt32
& aFrameIndex
,
1077 PRInt32
& aLineNeedsUpdate
)
1079 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
1080 NS_PRECONDITION(aFrame
, "aFrame is null");
1081 NS_ASSERTION(!aFrame
->GetPrevInFlow(),
1082 "Calling EnsureBidiContinuation on non-first-in-flow");
1084 *aNewFrame
= nsnull
;
1085 nsBidiLevel embeddingLevel
= NS_GET_EMBEDDING_LEVEL(aFrame
);
1086 nsBidiLevel baseLevel
= NS_GET_BASE_LEVEL(aFrame
);
1087 nsCharType charType
= (nsCharType
)NS_PTR_TO_INT32(aFrame
->GetProperty(nsGkAtoms::charType
));
1089 // Skip fluid continuations
1090 while (aFrameIndex
+ 1 < mLogicalFrames
.Count()) {
1091 nsIFrame
* frame
= (nsIFrame
*)mLogicalFrames
[aFrameIndex
+ 1];
1092 if (frame
->GetPrevInFlow() != aFrame
) {
1093 // If we found a non-fluid continuation, use it
1094 if (frame
->GetPrevContinuation() == aFrame
) {
1097 // The frame we found might be on another line. If so, the line iterator
1098 // should be updated.
1099 aLineNeedsUpdate
= PR_TRUE
;
1103 frame
->SetProperty(nsGkAtoms::embeddingLevel
, NS_INT32_TO_PTR(embeddingLevel
));
1104 frame
->SetProperty(nsGkAtoms::baseLevel
, NS_INT32_TO_PTR(baseLevel
));
1105 frame
->SetProperty(nsGkAtoms::charType
, NS_INT32_TO_PTR(charType
));
1106 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
1108 aFrame
->AdjustOffsetsForBidi(aStart
, aStart
);
1112 aFrame
->AdjustOffsetsForBidi(aStart
, aEnd
);
1114 mSuccess
= CreateBidiContinuation(aFrame
, aNewFrame
);
1115 if (NS_FAILED(mSuccess
) ) {
1124 nsBidiPresUtils::RemoveBidiContinuation(nsIFrame
* aFrame
,
1125 PRInt32 aFirstIndex
,
1127 PRInt32
& aOffset
) const
1132 nsBidiLevel embeddingLevel
= (nsCharType
)NS_PTR_TO_INT32(aFrame
->GetProperty(nsGkAtoms::embeddingLevel
, &rv
));
1133 NS_ASSERTION(NS_SUCCEEDED(rv
), "embeddingLevel attribute missing from aFrame");
1134 nsBidiLevel baseLevel
= (nsCharType
)NS_PTR_TO_INT32(aFrame
->GetProperty(nsGkAtoms::baseLevel
, &rv
));
1135 NS_ASSERTION(NS_SUCCEEDED(rv
), "baseLevel attribute missing from aFrame");
1136 nsCharType charType
= (nsCharType
)NS_PTR_TO_INT32(aFrame
->GetProperty(nsGkAtoms::charType
, &rv
));
1137 NS_ASSERTION(NS_SUCCEEDED(rv
), "charType attribute missing from aFrame");
1139 for (PRInt32 index
= aFirstIndex
+ 1; index
<= aLastIndex
; index
++) {
1140 nsIFrame
* frame
= (nsIFrame
*) mLogicalFrames
[index
];
1141 if (nsGkAtoms::directionalFrame
== frame
->GetType()) {
1146 // Make the frame and its continuation ancestors fluid,
1147 // so they can be reused or deleted by normal reflow code
1148 frame
->SetProperty(nsGkAtoms::embeddingLevel
, NS_INT32_TO_PTR(embeddingLevel
));
1149 frame
->SetProperty(nsGkAtoms::baseLevel
, NS_INT32_TO_PTR(baseLevel
));
1150 frame
->SetProperty(nsGkAtoms::charType
, NS_INT32_TO_PTR(charType
));
1151 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
1153 nsIFrame
* prev
= frame
->GetPrevContinuation();
1155 NS_ASSERTION (!frame
->GetPrevInFlow() || frame
->GetPrevInFlow() == prev
,
1156 "prev-in-flow is not prev continuation!");
1157 frame
->SetPrevInFlow(prev
);
1159 NS_ASSERTION (!prev
->GetNextInFlow() || prev
->GetNextInFlow() == frame
,
1160 "next-in-flow is not next continuation!");
1161 prev
->SetNextInFlow(frame
);
1163 frame
= frame
->GetParent();
1173 nsBidiPresUtils::FormatUnicodeText(nsPresContext
* aPresContext
,
1175 PRInt32
& aTextLength
,
1176 nsCharType aCharType
,
1179 NS_ASSERTION(aIsOddLevel
== 0 || aIsOddLevel
== 1, "aIsOddLevel should be 0 or 1");
1180 nsresult rv
= NS_OK
;
1182 //adjusted for correct numeral shaping
1183 PRUint32 bidiOptions
= aPresContext
->GetBidi();
1184 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions
)) {
1186 case IBMBIDI_NUMERAL_HINDI
:
1187 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1190 case IBMBIDI_NUMERAL_ARABIC
:
1191 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1194 case IBMBIDI_NUMERAL_REGULAR
:
1196 switch (aCharType
) {
1198 case eCharType_EuropeanNumber
:
1199 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1202 case eCharType_ArabicNumber
:
1203 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1211 case IBMBIDI_NUMERAL_HINDICONTEXT
:
1212 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1213 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1214 else if (eCharType_EuropeanNumber
== aCharType
)
1215 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1218 case IBMBIDI_NUMERAL_NOMINAL
:
1223 StripBidiControlCharacters(aText
, aTextLength
);
1228 nsBidiPresUtils::StripBidiControlCharacters(PRUnichar
* aText
,
1229 PRInt32
& aTextLength
) const
1231 if ( (nsnull
== aText
) || (aTextLength
< 1) ) {
1235 PRInt32 stripLen
= 0;
1237 for (PRInt32 i
= 0; i
< aTextLength
; i
++) {
1238 // XXX: This silently ignores surrogate characters.
1239 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1240 if (IsBidiControl((PRUint32
)aText
[i
])) {
1244 aText
[i
- stripLen
] = aText
[i
];
1247 aTextLength
-= stripLen
;
1250 #if 0 // XXX: for the future use ???
1252 RemoveDiacritics(PRUnichar
* aText
,
1253 PRInt32
& aTextLength
)
1255 if (aText
&& (aTextLength
> 0) ) {
1258 for (PRInt32 i
= 0; i
< aTextLength
&& aText
[i
]; i
++) {
1259 if (IS_BIDI_DIACRITIC(aText
[i
]) ) {
1263 aText
[i
- offset
] = aText
[i
];
1265 aTextLength
= i
- offset
;
1266 aText
[aTextLength
] = 0;
1272 nsBidiPresUtils::CalculateCharType(PRInt32
& aOffset
,
1273 PRInt32 aCharTypeLimit
,
1275 PRInt32
& aRunLength
,
1278 PRUint8
& aPrevCharType
) const
1281 PRBool strongTypeFound
= PR_FALSE
;
1283 nsCharType charType
;
1285 aCharType
= eCharType_OtherNeutral
;
1287 for (offset
= aOffset
; offset
< aCharTypeLimit
; offset
++) {
1288 // Make sure we give RTL chartype to all characters that would be classified
1289 // as Right-To-Left by a bidi platform.
1290 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1291 if (IS_HEBREW_CHAR(mBuffer
[offset
]) ) {
1292 charType
= eCharType_RightToLeft
;
1294 else if (IS_ARABIC_ALPHABETIC(mBuffer
[offset
]) ) {
1295 charType
= eCharType_RightToLeftArabic
;
1298 mBidiEngine
->GetCharTypeAt(offset
, &charType
);
1301 if (!CHARTYPE_IS_WEAK(charType
) ) {
1304 && (charType
!= aPrevCharType
)
1305 && (CHARTYPE_IS_RTL(charType
) || CHARTYPE_IS_RTL(aPrevCharType
) ) ) {
1306 // Stop at this point to ensure uni-directionality of the text
1307 // (from platform's point of view).
1308 // Also, don't mix Arabic and Hebrew content (since platform may
1309 // provide BIDI support to one of them only).
1310 aRunLength
= offset
- aOffset
;
1316 if ( (eCharType_RightToLeftArabic
== aPrevCharType
1317 || eCharType_ArabicNumber
== aPrevCharType
)
1318 && eCharType_EuropeanNumber
== charType
) {
1319 charType
= eCharType_ArabicNumber
;
1322 // Set PrevCharType to the last strong type in this frame
1323 // (for correct numeric shaping)
1324 aPrevCharType
= charType
;
1326 strongTypeFound
= PR_TRUE
;
1327 aCharType
= charType
;
1333 nsresult
nsBidiPresUtils::GetBidiEngine(nsBidi
** aBidiEngine
)
1335 nsresult rv
= NS_ERROR_FAILURE
;
1337 *aBidiEngine
= mBidiEngine
;
1343 nsresult
nsBidiPresUtils::ProcessText(const PRUnichar
* aText
,
1345 nsBidiDirection aBaseDirection
,
1346 nsPresContext
* aPresContext
,
1347 BidiProcessor
& aprocessor
,
1349 nsBidiPositionResolve
* aPosResolve
,
1350 PRInt32 aPosResolveCount
,
1353 NS_ASSERTION((aPosResolve
== nsnull
) != (aPosResolveCount
> 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1357 mBuffer
.Assign(aText
, aLength
);
1359 nsresult rv
= mBidiEngine
->SetPara(mBuffer
.get(), aLength
, aBaseDirection
, nsnull
);
1363 rv
= mBidiEngine
->CountRuns(&runCount
);
1367 nscoord xOffset
= 0;
1368 nscoord width
, xEndRun
;
1369 nscoord totalWidth
= 0;
1370 PRInt32 i
, start
, limit
, length
;
1371 PRUint32 visualStart
= 0;
1373 PRUint8 prevType
= eCharType_LeftToRight
;
1376 for(int nPosResolve
=0; nPosResolve
< aPosResolveCount
; ++nPosResolve
)
1378 aPosResolve
[nPosResolve
].visualIndex
= kNotFound
;
1379 aPosResolve
[nPosResolve
].visualLeftTwips
= kNotFound
;
1382 for (i
= 0; i
< runCount
; i
++) {
1383 rv
= mBidiEngine
->GetVisualRun(i
, &start
, &length
, &aBaseDirection
);
1387 rv
= mBidiEngine
->GetLogicalRun(start
, &limit
, &level
);
1391 PRInt32 subRunLength
= limit
- start
;
1392 PRInt32 lineOffset
= start
;
1393 PRInt32 typeLimit
= PR_MIN(limit
, aLength
);
1394 PRInt32 subRunCount
= 1;
1395 PRInt32 subRunLimit
= typeLimit
;
1398 * If |level| is even, i.e. the direction of the run is left-to-right, we
1399 * render the subruns from left to right and increment the x-coordinate
1400 * |xOffset| by the width of each subrun after rendering.
1402 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1403 * render the subruns from right to left. We begin by incrementing |xOffset| by
1404 * the width of the whole run, and then decrement it by the width of each
1405 * subrun before rendering. After rendering all the subruns, we restore the
1406 * x-coordinate of the end of the run for the start of the next run.
1410 aprocessor
.SetText(aText
+ start
, subRunLength
, nsBidiDirection(level
& 1));
1411 width
= aprocessor
.GetWidth();
1416 while (subRunCount
> 0) {
1417 // CalculateCharType can increment subRunCount if the run
1418 // contains mixed character types
1419 CalculateCharType(lineOffset
, typeLimit
, subRunLimit
, subRunLength
, subRunCount
, charType
, prevType
);
1421 nsAutoString runVisualText
;
1422 runVisualText
.Assign(aText
+ start
, subRunLength
);
1423 if (runVisualText
.Length() < subRunLength
)
1424 return NS_ERROR_OUT_OF_MEMORY
;
1425 FormatUnicodeText(aPresContext
, runVisualText
.BeginWriting(), subRunLength
,
1426 (nsCharType
)charType
, level
& 1);
1428 aprocessor
.SetText(runVisualText
.get(), subRunLength
, nsBidiDirection(level
& 1));
1429 width
= aprocessor
.GetWidth();
1430 totalWidth
+= width
;
1434 if (aMode
== MODE_DRAW
) {
1435 aprocessor
.DrawText(xOffset
, width
);
1439 * The caller may request to calculate the visual position of one
1440 * or more characters.
1442 for(int nPosResolve
=0; nPosResolve
<aPosResolveCount
; ++nPosResolve
)
1444 nsBidiPositionResolve
* posResolve
= &aPosResolve
[nPosResolve
];
1446 * Did we already resolve this position's visual metric? If so, skip.
1448 if (posResolve
->visualLeftTwips
!= kNotFound
)
1452 * First find out if the logical position is within this run.
1454 if (start
<= posResolve
->logicalIndex
&&
1455 start
+ subRunLength
> posResolve
->logicalIndex
) {
1457 * If this run is only one character long, we have an easy case:
1458 * the visual position is the x-coord of the start of the run
1459 * less the x-coord of the start of the whole text.
1461 if (subRunLength
== 1) {
1462 posResolve
->visualIndex
= visualStart
;
1463 posResolve
->visualLeftTwips
= xOffset
;
1466 * Otherwise, we need to measure the width of the run's part
1467 * which is to the visual left of the index.
1468 * In other words, the run is broken in two, around the logical index,
1469 * and we measure the part which is visually left.
1470 * If the run is right-to-left, this part will span from after the index
1471 * up to the end of the run; if it is left-to-right, this part will span
1472 * from the start of the run up to (and inclduing) the character before the index.
1476 // The position in the text where this run's "left part" begins.
1477 const PRUnichar
* visualLeftPart
;
1479 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
1480 posResolve
->visualIndex
= visualStart
+ (subRunLength
- (posResolve
->logicalIndex
+ 1 - start
));
1481 // Skipping to the "left part".
1482 visualLeftPart
= aText
+ posResolve
->logicalIndex
+ 1;
1485 posResolve
->visualIndex
= visualStart
+ (posResolve
->logicalIndex
- start
);
1486 // Skipping to the "left part".
1487 visualLeftPart
= aText
+ start
;
1489 // The delta between the start of the run and the left part's end.
1490 PRInt32 visualLeftLength
= posResolve
->visualIndex
- visualStart
;
1491 aprocessor
.SetText(visualLeftPart
, visualLeftLength
, nsBidiDirection(level
& 1));
1492 subWidth
= aprocessor
.GetWidth();
1493 posResolve
->visualLeftTwips
= xOffset
+ subWidth
;
1504 subRunLimit
= typeLimit
;
1505 subRunLength
= typeLimit
- lineOffset
;
1511 visualStart
+= length
;
1515 *aWidth
= totalWidth
;
1520 class NS_STACK_CLASS nsIRenderingContextBidiProcessor
: public nsBidiPresUtils::BidiProcessor
{
1522 nsIRenderingContextBidiProcessor(nsIRenderingContext
* aCtx
,
1524 : mCtx(aCtx
), mPt(aPt
) { }
1526 ~nsIRenderingContextBidiProcessor()
1528 mCtx
->SetRightToLeftText(PR_FALSE
);
1531 virtual void SetText(const PRUnichar
* aText
,
1533 nsBidiDirection aDirection
)
1535 mCtx
->SetTextRunRTL(aDirection
==NSBIDI_RTL
);
1540 virtual nscoord
GetWidth()
1543 mCtx
->GetWidth(mText
, mLength
, width
, nsnull
);
1547 virtual void DrawText(nscoord aXOffset
,
1550 mCtx
->DrawString(mText
, mLength
, mPt
.x
+ aXOffset
, mPt
.y
);
1555 nsIRenderingContext
* mCtx
;
1556 const PRUnichar
* mText
;
1558 nsBidiDirection mDirection
;
1561 nsresult
nsBidiPresUtils::ProcessTextForRenderingContext(const PRUnichar
* aText
,
1563 nsBidiDirection aBaseDirection
,
1564 nsPresContext
* aPresContext
,
1565 nsIRenderingContext
& aRenderingContext
,
1569 nsBidiPositionResolve
* aPosResolve
,
1570 PRInt32 aPosResolveCount
,
1573 nsIRenderingContextBidiProcessor
processor(&aRenderingContext
, nsPoint(aX
, aY
));
1575 return ProcessText(aText
, aLength
, aBaseDirection
, aPresContext
, processor
,
1576 aMode
, aPosResolve
, aPosResolveCount
, aWidth
);