Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsBidiPresUtils_h___
8 #define nsBidiPresUtils_h___
10 #include "nsBidi.h"
11 #include "nsBidiUtils.h"
12 #include "nsHashKeys.h"
13 #include "nsCoord.h"
15 #ifdef DrawText
16 #undef DrawText
17 #endif
19 struct BidiParagraphData;
20 struct BidiLineData;
21 class nsIFrame;
22 class nsBlockFrame;
23 class nsPresContext;
24 class nsRenderingContext;
25 class nsBlockInFlowLineIterator;
26 class nsStyleContext;
27 template<class T> class nsTHashtable;
28 namespace mozilla { class WritingMode; }
30 /**
31 * A structure representing some continuation state for each frame on the line,
32 * used to determine the first and the last continuation frame for each
33 * continuation chain on the line.
34 */
35 struct nsFrameContinuationState : public nsVoidPtrHashKey
36 {
37 nsFrameContinuationState(const void *aFrame) : nsVoidPtrHashKey(aFrame) {}
39 /**
40 * The first visual frame in the continuation chain containing this frame, or
41 * nullptr if this frame is the first visual frame in the chain.
42 */
43 nsIFrame* mFirstVisualFrame;
45 /**
46 * The number of frames in the continuation chain containing this frame, if
47 * this frame is the first visual frame of the chain, or 0 otherwise.
48 */
49 uint32_t mFrameCount;
51 /**
52 * TRUE if this frame is the first visual frame of its continuation chain on
53 * this line and the chain has some frames on the previous lines.
54 */
55 bool mHasContOnPrevLines;
57 /**
58 * TRUE if this frame is the first visual frame of its continuation chain on
59 * this line and the chain has some frames left for next lines.
60 */
61 bool mHasContOnNextLines;
62 };
64 /*
65 * Following type is used to pass needed hashtable to reordering methods
66 */
67 typedef nsTHashtable<nsFrameContinuationState> nsContinuationStates;
69 /**
70 * A structure representing a logical position which should be resolved
71 * into its visual position during BiDi processing.
72 */
73 struct nsBidiPositionResolve
74 {
75 // [in] Logical index within string.
76 int32_t logicalIndex;
77 // [out] Visual index within string.
78 // If the logical position was not found, set to kNotFound.
79 int32_t visualIndex;
80 // [out] Visual position of the character, from the left (on the X axis), in twips.
81 // 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.
82 // If the logical position was not found, set to kNotFound.
83 int32_t visualLeftTwips;
84 // [out] Visual width of the character, in twips.
85 // If the logical position was not found, set to kNotFound.
86 int32_t visualWidth;
87 };
89 class nsBidiPresUtils {
90 public:
91 nsBidiPresUtils();
92 ~nsBidiPresUtils();
94 /**
95 * Interface for the processor used by ProcessText. Used by process text to
96 * collect information about the width of subruns and to notify where each
97 * subrun should be rendered.
98 */
99 class BidiProcessor {
100 public:
101 virtual ~BidiProcessor() { }
103 /**
104 * Sets the current text with the given length and the given direction.
105 *
106 * @remark The reason that the function gives a string instead of an index
107 * is that ProcessText copies and modifies the string passed to it, so
108 * passing an index would be impossible.
109 *
110 * @param aText The string of text.
111 * @param aLength The length of the string of text.
112 * @param aDirection The direction of the text. The string will never have
113 * mixed direction.
114 */
115 virtual void SetText(const char16_t* aText,
116 int32_t aLength,
117 nsBidiDirection aDirection) = 0;
119 /**
120 * Returns the measured width of the text given in SetText. If SetText was
121 * not called with valid parameters, the result of this call is undefined.
122 * This call is guaranteed to only be called once between SetText calls.
123 * Will be invoked before DrawText.
124 */
125 virtual nscoord GetWidth() = 0;
127 /**
128 * Draws the text given in SetText to a rendering context. If SetText was
129 * not called with valid parameters, the result of this call is undefined.
130 * This call is guaranteed to only be called once between SetText calls.
131 *
132 * @param aXOffset The offset of the left side of the substring to be drawn
133 * from the beginning of the overall string passed to ProcessText.
134 * @param aWidth The width returned by GetWidth.
135 */
136 virtual void DrawText(nscoord aXOffset,
137 nscoord aWidth) = 0;
138 };
140 /**
141 * Make Bidi engine calculate the embedding levels of the frames that are
142 * descendants of a given block frame.
143 *
144 * @param aBlockFrame The block frame
145 *
146 * @lina 06/18/2000
147 */
148 static nsresult Resolve(nsBlockFrame* aBlockFrame);
149 static nsresult ResolveParagraph(nsBlockFrame* aBlockFrame,
150 BidiParagraphData* aBpd);
151 static void ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame,
152 BidiParagraphData* aBpd);
154 /**
155 * Reorder this line using Bidi engine.
156 * Update frame array, following the new visual sequence.
157 *
158 * @lina 05/02/2000
159 */
160 static void ReorderFrames(nsIFrame* aFirstFrameOnLine,
161 int32_t aNumFramesOnLine,
162 mozilla::WritingMode aLineWM,
163 nscoord& aLineWidth);
165 /**
166 * Format Unicode text, taking into account bidi capabilities
167 * of the platform. The formatting includes: reordering, Arabic shaping,
168 * symmetric and numeric swapping, removing control characters.
169 *
170 * @lina 06/18/2000
171 */
172 static nsresult FormatUnicodeText(nsPresContext* aPresContext,
173 char16_t* aText,
174 int32_t& aTextLength,
175 nsCharType aCharType,
176 bool aIsOddLevel);
178 /**
179 * Reorder plain text using the Unicode Bidi algorithm and send it to
180 * a rendering context for rendering.
181 *
182 * @param[in] aText the string to be rendered (in logical order)
183 * @param aLength the number of characters in the string
184 * @param aBaseLevel the base embedding level of the string
185 * odd values are right-to-left; even values are left-to-right, plus special
186 * constants as follows (defined in nsBidi.h)
187 * NSBIDI_LTR - left-to-right string
188 * NSBIDI_RTL - right-to-left string
189 * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
190 * default is left-to-right
191 * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
192 * default is right-to-left
193 *
194 * @param aPresContext the presentation context
195 * @param aRenderingContext the rendering context to render to
196 * @param aTextRunConstructionContext the rendering context to be used to construct the textrun (affects font hinting)
197 * @param aX the x-coordinate to render the string
198 * @param aY the y-coordinate to render the string
199 * @param[in,out] aPosResolve array of logical positions to resolve into visual positions; can be nullptr if this functionality is not required
200 * @param aPosResolveCount number of items in the aPosResolve array
201 */
202 static nsresult RenderText(const char16_t* aText,
203 int32_t aLength,
204 nsBidiLevel aBaseLevel,
205 nsPresContext* aPresContext,
206 nsRenderingContext& aRenderingContext,
207 nsRenderingContext& aTextRunConstructionContext,
208 nscoord aX,
209 nscoord aY,
210 nsBidiPositionResolve* aPosResolve = nullptr,
211 int32_t aPosResolveCount = 0)
212 {
213 return ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
214 aTextRunConstructionContext, MODE_DRAW, aX, aY, aPosResolve, aPosResolveCount, nullptr);
215 }
217 static nscoord MeasureTextWidth(const char16_t* aText,
218 int32_t aLength,
219 nsBidiLevel aBaseLevel,
220 nsPresContext* aPresContext,
221 nsRenderingContext& aRenderingContext)
222 {
223 nscoord length;
224 nsresult rv = ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext,
225 aRenderingContext, aRenderingContext,
226 MODE_MEASURE, 0, 0, nullptr, 0, &length);
227 return NS_SUCCEEDED(rv) ? length : 0;
228 }
230 /**
231 * Check if a line is reordered, i.e., if the child frames are not
232 * all laid out left-to-right.
233 * @param aFirstFrameOnLine : first frame of the line to be tested
234 * @param aNumFramesOnLine : number of frames on this line
235 * @param[out] aLeftMost : leftmost frame on this line
236 * @param[out] aRightMost : rightmost frame on this line
237 */
238 static bool CheckLineOrder(nsIFrame* aFirstFrameOnLine,
239 int32_t aNumFramesOnLine,
240 nsIFrame** aLeftmost,
241 nsIFrame** aRightmost);
243 /**
244 * Get the frame to the right of the given frame, on the same line.
245 * @param aFrame : We're looking for the frame to the right of this frame.
246 * If null, return the leftmost frame on the line.
247 * @param aFirstFrameOnLine : first frame of the line to be tested
248 * @param aNumFramesOnLine : number of frames on this line
249 */
250 static nsIFrame* GetFrameToRightOf(const nsIFrame* aFrame,
251 nsIFrame* aFirstFrameOnLine,
252 int32_t aNumFramesOnLine);
254 /**
255 * Get the frame to the left of the given frame, on the same line.
256 * @param aFrame : We're looking for the frame to the left of this frame.
257 * If null, return the rightmost frame on the line.
258 * @param aFirstFrameOnLine : first frame of the line to be tested
259 * @param aNumFramesOnLine : number of frames on this line
260 */
261 static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame,
262 nsIFrame* aFirstFrameOnLine,
263 int32_t aNumFramesOnLine);
265 static nsIFrame* GetFirstLeaf(nsIFrame* aFrame);
267 /**
268 * Get the bidi embedding level of the given (inline) frame.
269 */
270 static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame);
272 /**
273 * Get the paragraph depth of the given (inline) frame.
274 */
275 static uint8_t GetParagraphDepth(nsIFrame* aFrame);
277 /**
278 * Get the bidi base level of the given (inline) frame.
279 */
280 static nsBidiLevel GetFrameBaseLevel(nsIFrame* aFrame);
282 enum Mode { MODE_DRAW, MODE_MEASURE };
284 /**
285 * Reorder plain text using the Unicode Bidi algorithm and send it to
286 * a processor for rendering or measuring
287 *
288 * @param[in] aText the string to be processed (in logical order)
289 * @param aLength the number of characters in the string
290 * @param aBaseLevel the base embedding level of the string
291 * odd values are right-to-left; even values are left-to-right, plus special
292 * constants as follows (defined in nsBidi.h)
293 * NSBIDI_LTR - left-to-right string
294 * NSBIDI_RTL - right-to-left string
295 * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
296 * default is left-to-right
297 * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
298 * default is right-to-left
299 *
300 * @param aPresContext the presentation context
301 * @param aprocessor the bidi processor
302 * @param aMode the operation to process
303 * MODE_DRAW - invokes DrawText on the processor for each substring
304 * MODE_MEASURE - does not invoke DrawText on the processor
305 * Note that the string is always measured, regardless of mode
306 * @param[in,out] aPosResolve array of logical positions to resolve into
307 * visual positions; can be nullptr if this functionality is not required
308 * @param aPosResolveCount number of items in the aPosResolve array
309 * @param[out] aWidth Pointer to where the width will be stored (may be null)
310 */
311 static nsresult ProcessText(const char16_t* aText,
312 int32_t aLength,
313 nsBidiLevel aBaseLevel,
314 nsPresContext* aPresContext,
315 BidiProcessor& aprocessor,
316 Mode aMode,
317 nsBidiPositionResolve* aPosResolve,
318 int32_t aPosResolveCount,
319 nscoord* aWidth,
320 nsBidi* aBidiEngine);
322 /**
323 * Make a copy of a string, converting from logical to visual order
324 *
325 * @param aSource the source string
326 * @param aDest the destination string
327 * @param aBaseDirection the base direction of the string
328 * (NSBIDI_LTR or NSBIDI_RTL to force the base direction;
329 * NSBIDI_DEFAULT_LTR or NSBIDI_DEFAULT_RTL to let the bidi engine
330 * determine the direction from rules P2 and P3 of the bidi algorithm.
331 * @see nsBidi::GetPara
332 * @param aOverride if TRUE, the text has a bidi override, according to
333 * the direction in aDir
334 */
335 static void CopyLogicalToVisual(const nsAString& aSource,
336 nsAString& aDest,
337 nsBidiLevel aBaseDirection,
338 bool aOverride);
340 /**
341 * Use style attributes to determine the base paragraph level to pass to the
342 * bidi algorithm.
343 *
344 * If |unicode-bidi| is set to "[-moz-]plaintext", returns NSBIDI_DEFAULT_LTR,
345 * in other words the direction is determined from the first strong character
346 * in the text according to rules P2 and P3 of the bidi algorithm, or LTR if
347 * there is no strong character.
348 *
349 * Otherwise returns NSBIDI_LTR or NSBIDI_RTL depending on the value of
350 * |direction|
351 */
352 static nsBidiLevel BidiLevelFromStyle(nsStyleContext* aStyleContext);
354 private:
355 static nsresult
356 ProcessTextForRenderingContext(const char16_t* aText,
357 int32_t aLength,
358 nsBidiLevel aBaseLevel,
359 nsPresContext* aPresContext,
360 nsRenderingContext& aRenderingContext,
361 nsRenderingContext& aTextRunConstructionContext,
362 Mode aMode,
363 nscoord aX, // DRAW only
364 nscoord aY, // DRAW only
365 nsBidiPositionResolve* aPosResolve, /* may be null */
366 int32_t aPosResolveCount,
367 nscoord* aWidth /* may be null */);
369 /**
370 * Traverse the child frames of the block element and:
371 * Set up an array of the frames in logical order
372 * Create a string containing the text content of all the frames
373 * If we encounter content that requires us to split the element into more
374 * than one paragraph for bidi resolution, resolve the paragraph up to that
375 * point.
376 */
377 static void TraverseFrames(nsBlockFrame* aBlockFrame,
378 nsBlockInFlowLineIterator* aLineIter,
379 nsIFrame* aCurrentFrame,
380 BidiParagraphData* aBpd);
382 /*
383 * Position aFrame and its descendants to their visual places. Also if aFrame
384 * is not leaf, resize it to embrace its children.
385 *
386 * @param aFrame The frame which itself and its children are
387 * going to be repositioned
388 * @param aIsEvenLevel TRUE means the embedding level of this frame
389 * is even (LTR)
390 * @param[in,out] aStart IN value is the starting position of aFrame
391 * (without considering its inline-start margin)
392 * OUT value will be the ending position of aFrame
393 * (after adding its inline-end margin)
394 * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState
395 */
396 static void RepositionFrame(nsIFrame* aFrame,
397 bool aIsEvenLevel,
398 nscoord& aStart,
399 nsContinuationStates* aContinuationStates,
400 mozilla::WritingMode aLineWM,
401 nscoord& aLineWidth);
403 /*
404 * Initialize the continuation state(nsFrameContinuationState) to
405 * (nullptr, 0) for aFrame and its descendants.
406 *
407 * @param aFrame The frame which itself and its descendants will
408 * be initialized
409 * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState
410 */
411 static void InitContinuationStates(nsIFrame* aFrame,
412 nsContinuationStates* aContinuationStates);
414 /*
415 * Determine if aFrame is leftmost or rightmost, and set aIsLeftMost and
416 * aIsRightMost values. Also set continuation states of aContinuationStates.
417 *
418 * A frame is leftmost if it's the first appearance of its continuation chain
419 * on the line and the chain is on its first line if it's LTR or the chain is
420 * on its last line if it's RTL.
421 * A frame is rightmost if it's the last appearance of its continuation chain
422 * on the line and the chain is on its first line if it's RTL or the chain is
423 * on its last line if it's LTR.
424 *
425 * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState
426 * @param[out] aIsLeftMost TRUE means aFrame is leftmost frame or continuation
427 * @param[out] aIsRightMost TRUE means aFrame is rightmost frame or continuation
428 */
429 static void IsFirstOrLast(nsIFrame* aFrame,
430 nsContinuationStates* aContinuationStates,
431 bool& aIsFirst /* out */,
432 bool& aIsLast /* out */);
434 /**
435 * Adjust frame positions following their visual order
436 *
437 * @param aFirstChild the first kid
438 *
439 * @lina 04/11/2000
440 */
441 static void RepositionInlineFrames(BidiLineData* aBld,
442 nsIFrame* aFirstChild,
443 mozilla::WritingMode aLineWM,
444 nscoord& aLineWidth);
446 /**
447 * Helper method for Resolve()
448 * Truncate a text frame to the end of a single-directional run and possibly
449 * create a continuation frame for the remainder of its content.
450 *
451 * @param aFrame the original frame
452 * @param aNewFrame [OUT] the new frame that was created
453 * @param aFrameIndex [IN/OUT] index of aFrame in mLogicalFrames
454 * @param aStart [IN] the start of the content mapped by aFrame (and
455 * any fluid continuations)
456 * @param aEnd [IN] the offset of the end of the single-directional
457 * text run.
458 * @see Resolve()
459 * @see RemoveBidiContinuation()
460 */
461 static inline
462 nsresult EnsureBidiContinuation(nsIFrame* aFrame,
463 nsIFrame** aNewFrame,
464 int32_t& aFrameIndex,
465 int32_t aStart,
466 int32_t aEnd);
468 /**
469 * Helper method for Resolve()
470 * Convert one or more bidi continuation frames created in a previous reflow by
471 * EnsureBidiContinuation() into fluid continuations.
472 * @param aFrame the frame whose continuations are to be removed
473 * @param aFirstIndex index of aFrame in mLogicalFrames
474 * @param aLastIndex index of the last frame to be removed
475 * @param aOffset [OUT] count of directional frames removed. Since
476 * directional frames have control characters
477 * corresponding to them in mBuffer, the pointers to
478 * mBuffer in Resolve() will need to be updated after
479 * deleting the frames.
480 *
481 * @see Resolve()
482 * @see EnsureBidiContinuation()
483 */
484 static void RemoveBidiContinuation(BidiParagraphData* aBpd,
485 nsIFrame* aFrame,
486 int32_t aFirstIndex,
487 int32_t aLastIndex,
488 int32_t& aOffset);
489 static void CalculateCharType(nsBidi* aBidiEngine,
490 const char16_t* aText,
491 int32_t& aOffset,
492 int32_t aCharTypeLimit,
493 int32_t& aRunLimit,
494 int32_t& aRunLength,
495 int32_t& aRunCount,
496 uint8_t& aCharType,
497 uint8_t& aPrevCharType);
499 static void StripBidiControlCharacters(char16_t* aText,
500 int32_t& aTextLength);
502 static bool WriteLogicalToVisual(const char16_t* aSrc,
503 uint32_t aSrcLength,
504 char16_t* aDest,
505 nsBidiLevel aBaseDirection,
506 nsBidi* aBidiEngine);
508 static void WriteReverse(const char16_t* aSrc,
509 uint32_t aSrcLength,
510 char16_t* aDest);
511 };
513 #endif /* nsBidiPresUtils_h___ */