michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsBidiPresUtils_h___ michael@0: #define nsBidiPresUtils_h___ michael@0: michael@0: #include "nsBidi.h" michael@0: #include "nsBidiUtils.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsCoord.h" michael@0: michael@0: #ifdef DrawText michael@0: #undef DrawText michael@0: #endif michael@0: michael@0: struct BidiParagraphData; michael@0: struct BidiLineData; michael@0: class nsIFrame; michael@0: class nsBlockFrame; michael@0: class nsPresContext; michael@0: class nsRenderingContext; michael@0: class nsBlockInFlowLineIterator; michael@0: class nsStyleContext; michael@0: template class nsTHashtable; michael@0: namespace mozilla { class WritingMode; } michael@0: michael@0: /** michael@0: * A structure representing some continuation state for each frame on the line, michael@0: * used to determine the first and the last continuation frame for each michael@0: * continuation chain on the line. michael@0: */ michael@0: struct nsFrameContinuationState : public nsVoidPtrHashKey michael@0: { michael@0: nsFrameContinuationState(const void *aFrame) : nsVoidPtrHashKey(aFrame) {} michael@0: michael@0: /** michael@0: * The first visual frame in the continuation chain containing this frame, or michael@0: * nullptr if this frame is the first visual frame in the chain. michael@0: */ michael@0: nsIFrame* mFirstVisualFrame; michael@0: michael@0: /** michael@0: * The number of frames in the continuation chain containing this frame, if michael@0: * this frame is the first visual frame of the chain, or 0 otherwise. michael@0: */ michael@0: uint32_t mFrameCount; michael@0: michael@0: /** michael@0: * TRUE if this frame is the first visual frame of its continuation chain on michael@0: * this line and the chain has some frames on the previous lines. michael@0: */ michael@0: bool mHasContOnPrevLines; michael@0: michael@0: /** michael@0: * TRUE if this frame is the first visual frame of its continuation chain on michael@0: * this line and the chain has some frames left for next lines. michael@0: */ michael@0: bool mHasContOnNextLines; michael@0: }; michael@0: michael@0: /* michael@0: * Following type is used to pass needed hashtable to reordering methods michael@0: */ michael@0: typedef nsTHashtable nsContinuationStates; michael@0: michael@0: /** michael@0: * A structure representing a logical position which should be resolved michael@0: * into its visual position during BiDi processing. michael@0: */ michael@0: struct nsBidiPositionResolve michael@0: { michael@0: // [in] Logical index within string. michael@0: int32_t logicalIndex; michael@0: // [out] Visual index within string. michael@0: // If the logical position was not found, set to kNotFound. michael@0: int32_t visualIndex; michael@0: // [out] Visual position of the character, from the left (on the X axis), in twips. michael@0: // Eessentially, this is the X position (relative to the rendering context) where the text was drawn + the font metric of the visual string to the left of the given logical position. michael@0: // If the logical position was not found, set to kNotFound. michael@0: int32_t visualLeftTwips; michael@0: // [out] Visual width of the character, in twips. michael@0: // If the logical position was not found, set to kNotFound. michael@0: int32_t visualWidth; michael@0: }; michael@0: michael@0: class nsBidiPresUtils { michael@0: public: michael@0: nsBidiPresUtils(); michael@0: ~nsBidiPresUtils(); michael@0: michael@0: /** michael@0: * Interface for the processor used by ProcessText. Used by process text to michael@0: * collect information about the width of subruns and to notify where each michael@0: * subrun should be rendered. michael@0: */ michael@0: class BidiProcessor { michael@0: public: michael@0: virtual ~BidiProcessor() { } michael@0: michael@0: /** michael@0: * Sets the current text with the given length and the given direction. michael@0: * michael@0: * @remark The reason that the function gives a string instead of an index michael@0: * is that ProcessText copies and modifies the string passed to it, so michael@0: * passing an index would be impossible. michael@0: * michael@0: * @param aText The string of text. michael@0: * @param aLength The length of the string of text. michael@0: * @param aDirection The direction of the text. The string will never have michael@0: * mixed direction. michael@0: */ michael@0: virtual void SetText(const char16_t* aText, michael@0: int32_t aLength, michael@0: nsBidiDirection aDirection) = 0; michael@0: michael@0: /** michael@0: * Returns the measured width of the text given in SetText. If SetText was michael@0: * not called with valid parameters, the result of this call is undefined. michael@0: * This call is guaranteed to only be called once between SetText calls. michael@0: * Will be invoked before DrawText. michael@0: */ michael@0: virtual nscoord GetWidth() = 0; michael@0: michael@0: /** michael@0: * Draws the text given in SetText to a rendering context. If SetText was michael@0: * not called with valid parameters, the result of this call is undefined. michael@0: * This call is guaranteed to only be called once between SetText calls. michael@0: * michael@0: * @param aXOffset The offset of the left side of the substring to be drawn michael@0: * from the beginning of the overall string passed to ProcessText. michael@0: * @param aWidth The width returned by GetWidth. michael@0: */ michael@0: virtual void DrawText(nscoord aXOffset, michael@0: nscoord aWidth) = 0; michael@0: }; michael@0: michael@0: /** michael@0: * Make Bidi engine calculate the embedding levels of the frames that are michael@0: * descendants of a given block frame. michael@0: * michael@0: * @param aBlockFrame The block frame michael@0: * michael@0: * @lina 06/18/2000 michael@0: */ michael@0: static nsresult Resolve(nsBlockFrame* aBlockFrame); michael@0: static nsresult ResolveParagraph(nsBlockFrame* aBlockFrame, michael@0: BidiParagraphData* aBpd); michael@0: static void ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame, michael@0: BidiParagraphData* aBpd); michael@0: michael@0: /** michael@0: * Reorder this line using Bidi engine. michael@0: * Update frame array, following the new visual sequence. michael@0: * michael@0: * @lina 05/02/2000 michael@0: */ michael@0: static void ReorderFrames(nsIFrame* aFirstFrameOnLine, michael@0: int32_t aNumFramesOnLine, michael@0: mozilla::WritingMode aLineWM, michael@0: nscoord& aLineWidth); michael@0: michael@0: /** michael@0: * Format Unicode text, taking into account bidi capabilities michael@0: * of the platform. The formatting includes: reordering, Arabic shaping, michael@0: * symmetric and numeric swapping, removing control characters. michael@0: * michael@0: * @lina 06/18/2000 michael@0: */ michael@0: static nsresult FormatUnicodeText(nsPresContext* aPresContext, michael@0: char16_t* aText, michael@0: int32_t& aTextLength, michael@0: nsCharType aCharType, michael@0: bool aIsOddLevel); michael@0: michael@0: /** michael@0: * Reorder plain text using the Unicode Bidi algorithm and send it to michael@0: * a rendering context for rendering. michael@0: * michael@0: * @param[in] aText the string to be rendered (in logical order) michael@0: * @param aLength the number of characters in the string michael@0: * @param aBaseLevel the base embedding level of the string michael@0: * odd values are right-to-left; even values are left-to-right, plus special michael@0: * constants as follows (defined in nsBidi.h) michael@0: * NSBIDI_LTR - left-to-right string michael@0: * NSBIDI_RTL - right-to-left string michael@0: * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character, michael@0: * default is left-to-right michael@0: * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character, michael@0: * default is right-to-left michael@0: * michael@0: * @param aPresContext the presentation context michael@0: * @param aRenderingContext the rendering context to render to michael@0: * @param aTextRunConstructionContext the rendering context to be used to construct the textrun (affects font hinting) michael@0: * @param aX the x-coordinate to render the string michael@0: * @param aY the y-coordinate to render the string michael@0: * @param[in,out] aPosResolve array of logical positions to resolve into visual positions; can be nullptr if this functionality is not required michael@0: * @param aPosResolveCount number of items in the aPosResolve array michael@0: */ michael@0: static nsresult RenderText(const char16_t* aText, michael@0: int32_t aLength, michael@0: nsBidiLevel aBaseLevel, michael@0: nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsRenderingContext& aTextRunConstructionContext, michael@0: nscoord aX, michael@0: nscoord aY, michael@0: nsBidiPositionResolve* aPosResolve = nullptr, michael@0: int32_t aPosResolveCount = 0) michael@0: { michael@0: return ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext, aRenderingContext, michael@0: aTextRunConstructionContext, MODE_DRAW, aX, aY, aPosResolve, aPosResolveCount, nullptr); michael@0: } michael@0: michael@0: static nscoord MeasureTextWidth(const char16_t* aText, michael@0: int32_t aLength, michael@0: nsBidiLevel aBaseLevel, michael@0: nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext) michael@0: { michael@0: nscoord length; michael@0: nsresult rv = ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext, michael@0: aRenderingContext, aRenderingContext, michael@0: MODE_MEASURE, 0, 0, nullptr, 0, &length); michael@0: return NS_SUCCEEDED(rv) ? length : 0; michael@0: } michael@0: michael@0: /** michael@0: * Check if a line is reordered, i.e., if the child frames are not michael@0: * all laid out left-to-right. michael@0: * @param aFirstFrameOnLine : first frame of the line to be tested michael@0: * @param aNumFramesOnLine : number of frames on this line michael@0: * @param[out] aLeftMost : leftmost frame on this line michael@0: * @param[out] aRightMost : rightmost frame on this line michael@0: */ michael@0: static bool CheckLineOrder(nsIFrame* aFirstFrameOnLine, michael@0: int32_t aNumFramesOnLine, michael@0: nsIFrame** aLeftmost, michael@0: nsIFrame** aRightmost); michael@0: michael@0: /** michael@0: * Get the frame to the right of the given frame, on the same line. michael@0: * @param aFrame : We're looking for the frame to the right of this frame. michael@0: * If null, return the leftmost frame on the line. michael@0: * @param aFirstFrameOnLine : first frame of the line to be tested michael@0: * @param aNumFramesOnLine : number of frames on this line michael@0: */ michael@0: static nsIFrame* GetFrameToRightOf(const nsIFrame* aFrame, michael@0: nsIFrame* aFirstFrameOnLine, michael@0: int32_t aNumFramesOnLine); michael@0: michael@0: /** michael@0: * Get the frame to the left of the given frame, on the same line. michael@0: * @param aFrame : We're looking for the frame to the left of this frame. michael@0: * If null, return the rightmost frame on the line. michael@0: * @param aFirstFrameOnLine : first frame of the line to be tested michael@0: * @param aNumFramesOnLine : number of frames on this line michael@0: */ michael@0: static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame, michael@0: nsIFrame* aFirstFrameOnLine, michael@0: int32_t aNumFramesOnLine); michael@0: michael@0: static nsIFrame* GetFirstLeaf(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Get the bidi embedding level of the given (inline) frame. michael@0: */ michael@0: static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Get the paragraph depth of the given (inline) frame. michael@0: */ michael@0: static uint8_t GetParagraphDepth(nsIFrame* aFrame); michael@0: michael@0: /** michael@0: * Get the bidi base level of the given (inline) frame. michael@0: */ michael@0: static nsBidiLevel GetFrameBaseLevel(nsIFrame* aFrame); michael@0: michael@0: enum Mode { MODE_DRAW, MODE_MEASURE }; michael@0: michael@0: /** michael@0: * Reorder plain text using the Unicode Bidi algorithm and send it to michael@0: * a processor for rendering or measuring michael@0: * michael@0: * @param[in] aText the string to be processed (in logical order) michael@0: * @param aLength the number of characters in the string michael@0: * @param aBaseLevel the base embedding level of the string michael@0: * odd values are right-to-left; even values are left-to-right, plus special michael@0: * constants as follows (defined in nsBidi.h) michael@0: * NSBIDI_LTR - left-to-right string michael@0: * NSBIDI_RTL - right-to-left string michael@0: * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character, michael@0: * default is left-to-right michael@0: * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character, michael@0: * default is right-to-left michael@0: * michael@0: * @param aPresContext the presentation context michael@0: * @param aprocessor the bidi processor michael@0: * @param aMode the operation to process michael@0: * MODE_DRAW - invokes DrawText on the processor for each substring michael@0: * MODE_MEASURE - does not invoke DrawText on the processor michael@0: * Note that the string is always measured, regardless of mode michael@0: * @param[in,out] aPosResolve array of logical positions to resolve into michael@0: * visual positions; can be nullptr if this functionality is not required michael@0: * @param aPosResolveCount number of items in the aPosResolve array michael@0: * @param[out] aWidth Pointer to where the width will be stored (may be null) michael@0: */ michael@0: static nsresult ProcessText(const char16_t* aText, michael@0: int32_t aLength, michael@0: nsBidiLevel aBaseLevel, michael@0: nsPresContext* aPresContext, michael@0: BidiProcessor& aprocessor, michael@0: Mode aMode, michael@0: nsBidiPositionResolve* aPosResolve, michael@0: int32_t aPosResolveCount, michael@0: nscoord* aWidth, michael@0: nsBidi* aBidiEngine); michael@0: michael@0: /** michael@0: * Make a copy of a string, converting from logical to visual order michael@0: * michael@0: * @param aSource the source string michael@0: * @param aDest the destination string michael@0: * @param aBaseDirection the base direction of the string michael@0: * (NSBIDI_LTR or NSBIDI_RTL to force the base direction; michael@0: * NSBIDI_DEFAULT_LTR or NSBIDI_DEFAULT_RTL to let the bidi engine michael@0: * determine the direction from rules P2 and P3 of the bidi algorithm. michael@0: * @see nsBidi::GetPara michael@0: * @param aOverride if TRUE, the text has a bidi override, according to michael@0: * the direction in aDir michael@0: */ michael@0: static void CopyLogicalToVisual(const nsAString& aSource, michael@0: nsAString& aDest, michael@0: nsBidiLevel aBaseDirection, michael@0: bool aOverride); michael@0: michael@0: /** michael@0: * Use style attributes to determine the base paragraph level to pass to the michael@0: * bidi algorithm. michael@0: * michael@0: * If |unicode-bidi| is set to "[-moz-]plaintext", returns NSBIDI_DEFAULT_LTR, michael@0: * in other words the direction is determined from the first strong character michael@0: * in the text according to rules P2 and P3 of the bidi algorithm, or LTR if michael@0: * there is no strong character. michael@0: * michael@0: * Otherwise returns NSBIDI_LTR or NSBIDI_RTL depending on the value of michael@0: * |direction| michael@0: */ michael@0: static nsBidiLevel BidiLevelFromStyle(nsStyleContext* aStyleContext); michael@0: michael@0: private: michael@0: static nsresult michael@0: ProcessTextForRenderingContext(const char16_t* aText, michael@0: int32_t aLength, michael@0: nsBidiLevel aBaseLevel, michael@0: nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsRenderingContext& aTextRunConstructionContext, michael@0: Mode aMode, michael@0: nscoord aX, // DRAW only michael@0: nscoord aY, // DRAW only michael@0: nsBidiPositionResolve* aPosResolve, /* may be null */ michael@0: int32_t aPosResolveCount, michael@0: nscoord* aWidth /* may be null */); michael@0: michael@0: /** michael@0: * Traverse the child frames of the block element and: michael@0: * Set up an array of the frames in logical order michael@0: * Create a string containing the text content of all the frames michael@0: * If we encounter content that requires us to split the element into more michael@0: * than one paragraph for bidi resolution, resolve the paragraph up to that michael@0: * point. michael@0: */ michael@0: static void TraverseFrames(nsBlockFrame* aBlockFrame, michael@0: nsBlockInFlowLineIterator* aLineIter, michael@0: nsIFrame* aCurrentFrame, michael@0: BidiParagraphData* aBpd); michael@0: michael@0: /* michael@0: * Position aFrame and its descendants to their visual places. Also if aFrame michael@0: * is not leaf, resize it to embrace its children. michael@0: * michael@0: * @param aFrame The frame which itself and its children are michael@0: * going to be repositioned michael@0: * @param aIsEvenLevel TRUE means the embedding level of this frame michael@0: * is even (LTR) michael@0: * @param[in,out] aStart IN value is the starting position of aFrame michael@0: * (without considering its inline-start margin) michael@0: * OUT value will be the ending position of aFrame michael@0: * (after adding its inline-end margin) michael@0: * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState michael@0: */ michael@0: static void RepositionFrame(nsIFrame* aFrame, michael@0: bool aIsEvenLevel, michael@0: nscoord& aStart, michael@0: nsContinuationStates* aContinuationStates, michael@0: mozilla::WritingMode aLineWM, michael@0: nscoord& aLineWidth); michael@0: michael@0: /* michael@0: * Initialize the continuation state(nsFrameContinuationState) to michael@0: * (nullptr, 0) for aFrame and its descendants. michael@0: * michael@0: * @param aFrame The frame which itself and its descendants will michael@0: * be initialized michael@0: * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState michael@0: */ michael@0: static void InitContinuationStates(nsIFrame* aFrame, michael@0: nsContinuationStates* aContinuationStates); michael@0: michael@0: /* michael@0: * Determine if aFrame is leftmost or rightmost, and set aIsLeftMost and michael@0: * aIsRightMost values. Also set continuation states of aContinuationStates. michael@0: * michael@0: * A frame is leftmost if it's the first appearance of its continuation chain michael@0: * on the line and the chain is on its first line if it's LTR or the chain is michael@0: * on its last line if it's RTL. michael@0: * A frame is rightmost if it's the last appearance of its continuation chain michael@0: * on the line and the chain is on its first line if it's RTL or the chain is michael@0: * on its last line if it's LTR. michael@0: * michael@0: * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState michael@0: * @param[out] aIsLeftMost TRUE means aFrame is leftmost frame or continuation michael@0: * @param[out] aIsRightMost TRUE means aFrame is rightmost frame or continuation michael@0: */ michael@0: static void IsFirstOrLast(nsIFrame* aFrame, michael@0: nsContinuationStates* aContinuationStates, michael@0: bool& aIsFirst /* out */, michael@0: bool& aIsLast /* out */); michael@0: michael@0: /** michael@0: * Adjust frame positions following their visual order michael@0: * michael@0: * @param aFirstChild the first kid michael@0: * michael@0: * @lina 04/11/2000 michael@0: */ michael@0: static void RepositionInlineFrames(BidiLineData* aBld, michael@0: nsIFrame* aFirstChild, michael@0: mozilla::WritingMode aLineWM, michael@0: nscoord& aLineWidth); michael@0: michael@0: /** michael@0: * Helper method for Resolve() michael@0: * Truncate a text frame to the end of a single-directional run and possibly michael@0: * create a continuation frame for the remainder of its content. michael@0: * michael@0: * @param aFrame the original frame michael@0: * @param aNewFrame [OUT] the new frame that was created michael@0: * @param aFrameIndex [IN/OUT] index of aFrame in mLogicalFrames michael@0: * @param aStart [IN] the start of the content mapped by aFrame (and michael@0: * any fluid continuations) michael@0: * @param aEnd [IN] the offset of the end of the single-directional michael@0: * text run. michael@0: * @see Resolve() michael@0: * @see RemoveBidiContinuation() michael@0: */ michael@0: static inline michael@0: nsresult EnsureBidiContinuation(nsIFrame* aFrame, michael@0: nsIFrame** aNewFrame, michael@0: int32_t& aFrameIndex, michael@0: int32_t aStart, michael@0: int32_t aEnd); michael@0: michael@0: /** michael@0: * Helper method for Resolve() michael@0: * Convert one or more bidi continuation frames created in a previous reflow by michael@0: * EnsureBidiContinuation() into fluid continuations. michael@0: * @param aFrame the frame whose continuations are to be removed michael@0: * @param aFirstIndex index of aFrame in mLogicalFrames michael@0: * @param aLastIndex index of the last frame to be removed michael@0: * @param aOffset [OUT] count of directional frames removed. Since michael@0: * directional frames have control characters michael@0: * corresponding to them in mBuffer, the pointers to michael@0: * mBuffer in Resolve() will need to be updated after michael@0: * deleting the frames. michael@0: * michael@0: * @see Resolve() michael@0: * @see EnsureBidiContinuation() michael@0: */ michael@0: static void RemoveBidiContinuation(BidiParagraphData* aBpd, michael@0: nsIFrame* aFrame, michael@0: int32_t aFirstIndex, michael@0: int32_t aLastIndex, michael@0: int32_t& aOffset); michael@0: static void CalculateCharType(nsBidi* aBidiEngine, michael@0: const char16_t* aText, michael@0: int32_t& aOffset, michael@0: int32_t aCharTypeLimit, michael@0: int32_t& aRunLimit, michael@0: int32_t& aRunLength, michael@0: int32_t& aRunCount, michael@0: uint8_t& aCharType, michael@0: uint8_t& aPrevCharType); michael@0: michael@0: static void StripBidiControlCharacters(char16_t* aText, michael@0: int32_t& aTextLength); michael@0: michael@0: static bool WriteLogicalToVisual(const char16_t* aSrc, michael@0: uint32_t aSrcLength, michael@0: char16_t* aDest, michael@0: nsBidiLevel aBaseDirection, michael@0: nsBidi* aBidiEngine); michael@0: michael@0: static void WriteReverse(const char16_t* aSrc, michael@0: uint32_t aSrcLength, michael@0: char16_t* aDest); michael@0: }; michael@0: michael@0: #endif /* nsBidiPresUtils_h___ */