|
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/. */ |
|
6 |
|
7 #ifndef nsBidiPresUtils_h___ |
|
8 #define nsBidiPresUtils_h___ |
|
9 |
|
10 #include "nsBidi.h" |
|
11 #include "nsBidiUtils.h" |
|
12 #include "nsHashKeys.h" |
|
13 #include "nsCoord.h" |
|
14 |
|
15 #ifdef DrawText |
|
16 #undef DrawText |
|
17 #endif |
|
18 |
|
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; } |
|
29 |
|
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) {} |
|
38 |
|
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; |
|
44 |
|
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; |
|
50 |
|
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; |
|
56 |
|
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 }; |
|
63 |
|
64 /* |
|
65 * Following type is used to pass needed hashtable to reordering methods |
|
66 */ |
|
67 typedef nsTHashtable<nsFrameContinuationState> nsContinuationStates; |
|
68 |
|
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 }; |
|
88 |
|
89 class nsBidiPresUtils { |
|
90 public: |
|
91 nsBidiPresUtils(); |
|
92 ~nsBidiPresUtils(); |
|
93 |
|
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() { } |
|
102 |
|
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; |
|
118 |
|
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; |
|
126 |
|
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 }; |
|
139 |
|
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); |
|
153 |
|
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); |
|
164 |
|
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); |
|
177 |
|
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 } |
|
216 |
|
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 } |
|
229 |
|
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); |
|
242 |
|
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); |
|
253 |
|
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); |
|
264 |
|
265 static nsIFrame* GetFirstLeaf(nsIFrame* aFrame); |
|
266 |
|
267 /** |
|
268 * Get the bidi embedding level of the given (inline) frame. |
|
269 */ |
|
270 static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame); |
|
271 |
|
272 /** |
|
273 * Get the paragraph depth of the given (inline) frame. |
|
274 */ |
|
275 static uint8_t GetParagraphDepth(nsIFrame* aFrame); |
|
276 |
|
277 /** |
|
278 * Get the bidi base level of the given (inline) frame. |
|
279 */ |
|
280 static nsBidiLevel GetFrameBaseLevel(nsIFrame* aFrame); |
|
281 |
|
282 enum Mode { MODE_DRAW, MODE_MEASURE }; |
|
283 |
|
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); |
|
321 |
|
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); |
|
339 |
|
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); |
|
353 |
|
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 */); |
|
368 |
|
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); |
|
381 |
|
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); |
|
402 |
|
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); |
|
413 |
|
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 */); |
|
433 |
|
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); |
|
445 |
|
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); |
|
467 |
|
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); |
|
498 |
|
499 static void StripBidiControlCharacters(char16_t* aText, |
|
500 int32_t& aTextLength); |
|
501 |
|
502 static bool WriteLogicalToVisual(const char16_t* aSrc, |
|
503 uint32_t aSrcLength, |
|
504 char16_t* aDest, |
|
505 nsBidiLevel aBaseDirection, |
|
506 nsBidi* aBidiEngine); |
|
507 |
|
508 static void WriteReverse(const char16_t* aSrc, |
|
509 uint32_t aSrcLength, |
|
510 char16_t* aDest); |
|
511 }; |
|
512 |
|
513 #endif /* nsBidiPresUtils_h___ */ |