Line data Source code
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 "gfxContext.h"
11 : #include "nsBidi.h"
12 : #include "nsBidiUtils.h"
13 : #include "nsHashKeys.h"
14 : #include "nsCoord.h"
15 :
16 : #ifdef DrawText
17 : #undef DrawText
18 : #endif
19 :
20 : struct BidiParagraphData;
21 : struct BidiLineData;
22 : class gfxContext;
23 : class nsFontMetrics;
24 : class nsIFrame;
25 : class nsBlockFrame;
26 : class nsPresContext;
27 : class nsBlockInFlowLineIterator;
28 : class nsStyleContext;
29 : struct nsSize;
30 : template<class T> class nsTHashtable;
31 : namespace mozilla {
32 : class WritingMode;
33 : class LogicalMargin;
34 : } // namespace mozilla
35 :
36 : /**
37 : * A structure representing some continuation state for each frame on the line,
38 : * used to determine the first and the last continuation frame for each
39 : * continuation chain on the line.
40 : */
41 0 : struct nsFrameContinuationState : public nsVoidPtrHashKey
42 : {
43 0 : explicit nsFrameContinuationState(const void *aFrame)
44 0 : : nsVoidPtrHashKey(aFrame)
45 0 : {}
46 :
47 : /**
48 : * The first visual frame in the continuation chain containing this frame, or
49 : * nullptr if this frame is the first visual frame in the chain.
50 : */
51 : nsIFrame* mFirstVisualFrame { nullptr };
52 :
53 : /**
54 : * The number of frames in the continuation chain containing this frame, if
55 : * this frame is the first visual frame of the chain, or 0 otherwise.
56 : */
57 : uint32_t mFrameCount { 0 };
58 :
59 : /**
60 : * TRUE if this frame is the first visual frame of its continuation chain on
61 : * this line and the chain has some frames on the previous lines.
62 : */
63 : bool mHasContOnPrevLines { false };
64 :
65 : /**
66 : * TRUE if this frame is the first visual frame of its continuation chain on
67 : * this line and the chain has some frames left for next lines.
68 : */
69 : bool mHasContOnNextLines { false };
70 : };
71 :
72 : /*
73 : * Following type is used to pass needed hashtable to reordering methods
74 : */
75 : typedef nsTHashtable<nsFrameContinuationState> nsContinuationStates;
76 :
77 : /**
78 : * A structure representing a logical position which should be resolved
79 : * into its visual position during BiDi processing.
80 : */
81 : struct nsBidiPositionResolve
82 : {
83 : // [in] Logical index within string.
84 : int32_t logicalIndex;
85 : // [out] Visual index within string.
86 : // If the logical position was not found, set to kNotFound.
87 : int32_t visualIndex;
88 : // [out] Visual position of the character, from the left (on the X axis), in twips.
89 : // 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.
90 : // If the logical position was not found, set to kNotFound.
91 : int32_t visualLeftTwips;
92 : // [out] Visual width of the character, in twips.
93 : // If the logical position was not found, set to kNotFound.
94 : int32_t visualWidth;
95 : };
96 :
97 : class nsBidiPresUtils {
98 : public:
99 : typedef mozilla::gfx::DrawTarget DrawTarget;
100 :
101 : nsBidiPresUtils();
102 : ~nsBidiPresUtils();
103 :
104 : /**
105 : * Interface for the processor used by ProcessText. Used by process text to
106 : * collect information about the width of subruns and to notify where each
107 : * subrun should be rendered.
108 : */
109 0 : class BidiProcessor {
110 : public:
111 0 : virtual ~BidiProcessor() { }
112 :
113 : /**
114 : * Sets the current text with the given length and the given direction.
115 : *
116 : * @remark The reason that the function gives a string instead of an index
117 : * is that ProcessText copies and modifies the string passed to it, so
118 : * passing an index would be impossible.
119 : *
120 : * @param aText The string of text.
121 : * @param aLength The length of the string of text.
122 : * @param aDirection The direction of the text. The string will never have
123 : * mixed direction.
124 : */
125 : virtual void SetText(const char16_t* aText,
126 : int32_t aLength,
127 : nsBidiDirection aDirection) = 0;
128 :
129 : /**
130 : * Returns the measured width of the text given in SetText. If SetText was
131 : * not called with valid parameters, the result of this call is undefined.
132 : * This call is guaranteed to only be called once between SetText calls.
133 : * Will be invoked before DrawText.
134 : */
135 : virtual nscoord GetWidth() = 0;
136 :
137 : /**
138 : * Draws the text given in SetText to a rendering context. If SetText was
139 : * not called with valid parameters, the result of this call is undefined.
140 : * This call is guaranteed to only be called once between SetText calls.
141 : *
142 : * @param aXOffset The offset of the left side of the substring to be drawn
143 : * from the beginning of the overall string passed to ProcessText.
144 : * @param aWidth The width returned by GetWidth.
145 : */
146 : virtual void DrawText(nscoord aXOffset,
147 : nscoord aWidth) = 0;
148 : };
149 :
150 : /**
151 : * Make Bidi engine calculate the embedding levels of the frames that are
152 : * descendants of a given block frame.
153 : *
154 : * @param aBlockFrame The block frame
155 : *
156 : * @lina 06/18/2000
157 : */
158 : static nsresult Resolve(nsBlockFrame* aBlockFrame);
159 : static nsresult ResolveParagraph(BidiParagraphData* aBpd);
160 : static void ResolveParagraphWithinBlock(BidiParagraphData* aBpd);
161 :
162 : /**
163 : * Reorder this line using Bidi engine.
164 : * Update frame array, following the new visual sequence.
165 : *
166 : * @return total inline size
167 : *
168 : * @lina 05/02/2000
169 : */
170 : static nscoord ReorderFrames(nsIFrame* aFirstFrameOnLine,
171 : int32_t aNumFramesOnLine,
172 : mozilla::WritingMode aLineWM,
173 : const nsSize& aContainerSize,
174 : nscoord aStart);
175 :
176 : /**
177 : * Format Unicode text, taking into account bidi capabilities
178 : * of the platform. The formatting includes: reordering, Arabic shaping,
179 : * symmetric and numeric swapping, removing control characters.
180 : *
181 : * @lina 06/18/2000
182 : */
183 : static nsresult FormatUnicodeText(nsPresContext* aPresContext,
184 : char16_t* aText,
185 : int32_t& aTextLength,
186 : nsCharType aCharType);
187 :
188 : /**
189 : * Reorder plain text using the Unicode Bidi algorithm and send it to
190 : * a rendering context for rendering.
191 : *
192 : * @param[in] aText the string to be rendered (in logical order)
193 : * @param aLength the number of characters in the string
194 : * @param aBaseLevel the base embedding level of the string
195 : * odd values are right-to-left; even values are left-to-right, plus special
196 : * constants as follows (defined in nsBidi.h)
197 : * NSBIDI_LTR - left-to-right string
198 : * NSBIDI_RTL - right-to-left string
199 : * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
200 : * default is left-to-right
201 : * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
202 : * default is right-to-left
203 : *
204 : * @param aPresContext the presentation context
205 : * @param aRenderingContext the rendering context to render to
206 : * @param aTextRunConstructionContext the rendering context to be used to construct the textrun (affects font hinting)
207 : * @param aX the x-coordinate to render the string
208 : * @param aY the y-coordinate to render the string
209 : * @param[in,out] aPosResolve array of logical positions to resolve into visual positions; can be nullptr if this functionality is not required
210 : * @param aPosResolveCount number of items in the aPosResolve array
211 : */
212 0 : static nsresult RenderText(const char16_t* aText,
213 : int32_t aLength,
214 : nsBidiLevel aBaseLevel,
215 : nsPresContext* aPresContext,
216 : gfxContext& aRenderingContext,
217 : DrawTarget* aTextRunConstructionDrawTarget,
218 : nsFontMetrics& aFontMetrics,
219 : nscoord aX,
220 : nscoord aY,
221 : nsBidiPositionResolve* aPosResolve = nullptr,
222 : int32_t aPosResolveCount = 0)
223 : {
224 0 : return ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
225 : aTextRunConstructionDrawTarget,
226 : aFontMetrics,
227 0 : MODE_DRAW, aX, aY, aPosResolve, aPosResolveCount, nullptr);
228 : }
229 :
230 0 : static nscoord MeasureTextWidth(const char16_t* aText,
231 : int32_t aLength,
232 : nsBidiLevel aBaseLevel,
233 : nsPresContext* aPresContext,
234 : gfxContext& aRenderingContext,
235 : nsFontMetrics& aFontMetrics)
236 : {
237 : nscoord length;
238 0 : nsresult rv = ProcessTextForRenderingContext(aText, aLength, aBaseLevel, aPresContext,
239 : aRenderingContext,
240 : aRenderingContext.GetDrawTarget(),
241 : aFontMetrics,
242 0 : MODE_MEASURE, 0, 0, nullptr, 0, &length);
243 0 : return NS_SUCCEEDED(rv) ? length : 0;
244 : }
245 :
246 : /**
247 : * Check if a line is reordered, i.e., if the child frames are not
248 : * all laid out left-to-right.
249 : * @param aFirstFrameOnLine : first frame of the line to be tested
250 : * @param aNumFramesOnLine : number of frames on this line
251 : * @param[out] aLeftMost : leftmost frame on this line
252 : * @param[out] aRightMost : rightmost frame on this line
253 : */
254 : static bool CheckLineOrder(nsIFrame* aFirstFrameOnLine,
255 : int32_t aNumFramesOnLine,
256 : nsIFrame** aLeftmost,
257 : nsIFrame** aRightmost);
258 :
259 : /**
260 : * Get the frame to the right of the given frame, on the same line.
261 : * @param aFrame : We're looking for the frame to the right of this frame.
262 : * If null, return the leftmost frame on the line.
263 : * @param aFirstFrameOnLine : first frame of the line to be tested
264 : * @param aNumFramesOnLine : number of frames on this line
265 : */
266 : static nsIFrame* GetFrameToRightOf(const nsIFrame* aFrame,
267 : nsIFrame* aFirstFrameOnLine,
268 : int32_t aNumFramesOnLine);
269 :
270 : /**
271 : * Get the frame to the left of the given frame, on the same line.
272 : * @param aFrame : We're looking for the frame to the left of this frame.
273 : * If null, return the rightmost frame on the line.
274 : * @param aFirstFrameOnLine : first frame of the line to be tested
275 : * @param aNumFramesOnLine : number of frames on this line
276 : */
277 : static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame,
278 : nsIFrame* aFirstFrameOnLine,
279 : int32_t aNumFramesOnLine);
280 :
281 : static nsIFrame* GetFirstLeaf(nsIFrame* aFrame);
282 :
283 : /**
284 : * Get the bidi data of the given (inline) frame.
285 : */
286 : static mozilla::FrameBidiData GetFrameBidiData(nsIFrame* aFrame);
287 :
288 : /**
289 : * Get the bidi embedding level of the given (inline) frame.
290 : */
291 : static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame);
292 :
293 : /**
294 : * Get the bidi base level of the given (inline) frame.
295 : */
296 : static nsBidiLevel GetFrameBaseLevel(nsIFrame* aFrame);
297 :
298 : /**
299 : * Get an nsBidiDirection representing the direction implied by the
300 : * bidi base level of the frame.
301 : * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
302 : * NSBIDI_MIXED will never be returned.
303 : */
304 0 : static nsBidiDirection ParagraphDirection(nsIFrame* aFrame) {
305 0 : return DIRECTION_FROM_LEVEL(GetFrameBaseLevel(aFrame));
306 : }
307 :
308 : /**
309 : * Get an nsBidiDirection representing the direction implied by the
310 : * bidi embedding level of the frame.
311 : * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
312 : * NSBIDI_MIXED will never be returned.
313 : */
314 0 : static nsBidiDirection FrameDirection(nsIFrame* aFrame) {
315 0 : return DIRECTION_FROM_LEVEL(GetFrameEmbeddingLevel(aFrame));
316 : }
317 :
318 0 : static bool IsFrameInParagraphDirection(nsIFrame* aFrame) {
319 0 : return ParagraphDirection(aFrame) == FrameDirection(aFrame);
320 : }
321 :
322 : enum Mode { MODE_DRAW, MODE_MEASURE };
323 :
324 : /**
325 : * Reorder plain text using the Unicode Bidi algorithm and send it to
326 : * a processor for rendering or measuring
327 : *
328 : * @param[in] aText the string to be processed (in logical order)
329 : * @param aLength the number of characters in the string
330 : * @param aBaseLevel the base embedding level of the string
331 : * odd values are right-to-left; even values are left-to-right, plus special
332 : * constants as follows (defined in nsBidi.h)
333 : * NSBIDI_LTR - left-to-right string
334 : * NSBIDI_RTL - right-to-left string
335 : * NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
336 : * default is left-to-right
337 : * NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
338 : * default is right-to-left
339 : *
340 : * @param aPresContext the presentation context
341 : * @param aprocessor the bidi processor
342 : * @param aMode the operation to process
343 : * MODE_DRAW - invokes DrawText on the processor for each substring
344 : * MODE_MEASURE - does not invoke DrawText on the processor
345 : * Note that the string is always measured, regardless of mode
346 : * @param[in,out] aPosResolve array of logical positions to resolve into
347 : * visual positions; can be nullptr if this functionality is not required
348 : * @param aPosResolveCount number of items in the aPosResolve array
349 : * @param[out] aWidth Pointer to where the width will be stored (may be null)
350 : */
351 : static nsresult ProcessText(const char16_t* aText,
352 : int32_t aLength,
353 : nsBidiLevel aBaseLevel,
354 : nsPresContext* aPresContext,
355 : BidiProcessor& aprocessor,
356 : Mode aMode,
357 : nsBidiPositionResolve* aPosResolve,
358 : int32_t aPosResolveCount,
359 : nscoord* aWidth,
360 : nsBidi* aBidiEngine);
361 :
362 : /**
363 : * Use style attributes to determine the base paragraph level to pass to the
364 : * bidi algorithm.
365 : *
366 : * If |unicode-bidi| is set to "[-moz-]plaintext", returns NSBIDI_DEFAULT_LTR,
367 : * in other words the direction is determined from the first strong character
368 : * in the text according to rules P2 and P3 of the bidi algorithm, or LTR if
369 : * there is no strong character.
370 : *
371 : * Otherwise returns NSBIDI_LTR or NSBIDI_RTL depending on the value of
372 : * |direction|
373 : */
374 : static nsBidiLevel BidiLevelFromStyle(nsStyleContext* aStyleContext);
375 :
376 : private:
377 : static nsresult
378 : ProcessTextForRenderingContext(const char16_t* aText,
379 : int32_t aLength,
380 : nsBidiLevel aBaseLevel,
381 : nsPresContext* aPresContext,
382 : gfxContext& aRenderingContext,
383 : DrawTarget* aTextRunConstructionDrawTarget,
384 : nsFontMetrics& aFontMetrics,
385 : Mode aMode,
386 : nscoord aX, // DRAW only
387 : nscoord aY, // DRAW only
388 : nsBidiPositionResolve* aPosResolve, /* may be null */
389 : int32_t aPosResolveCount,
390 : nscoord* aWidth /* may be null */);
391 :
392 : /**
393 : * Traverse the child frames of the block element and:
394 : * Set up an array of the frames in logical order
395 : * Create a string containing the text content of all the frames
396 : * If we encounter content that requires us to split the element into more
397 : * than one paragraph for bidi resolution, resolve the paragraph up to that
398 : * point.
399 : */
400 : static void TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
401 : nsIFrame* aCurrentFrame,
402 : BidiParagraphData* aBpd);
403 :
404 : /**
405 : * Perform a recursive "pre-traversal" of the child frames of a block or
406 : * inline container frame, to determine whether full bidi resolution is
407 : * actually needed.
408 : * This explores the same frames as TraverseFrames (above), but is less
409 : * expensive and may allow us to avoid performing the full TraverseFrames
410 : * operation.
411 : * @param aFirstChild frame to start traversal from
412 : * @param[in/out] aCurrContent the content node that we've most recently
413 : * scanned for RTL characters (so that when descendant frames refer
414 : * to the same content, we can avoid repeatedly scanning it).
415 : * @return true if it finds that bidi is (or may be) required,
416 : * false if no potentially-bidi content is present.
417 : */
418 : static bool ChildListMayRequireBidi(nsIFrame* aFirstChild,
419 : nsIContent** aCurrContent);
420 :
421 : /**
422 : * Position ruby content frames (ruby base/text frame).
423 : * Called from RepositionRubyFrame.
424 : */
425 : static void RepositionRubyContentFrame(
426 : nsIFrame* aFrame, mozilla::WritingMode aFrameWM,
427 : const mozilla::LogicalMargin& aBorderPadding);
428 :
429 : /*
430 : * Position ruby frames. Called from RepositionFrame.
431 : */
432 : static nscoord RepositionRubyFrame(
433 : nsIFrame* aFrame,
434 : const nsContinuationStates* aContinuationStates,
435 : const mozilla::WritingMode aContainerWM,
436 : const mozilla::LogicalMargin& aBorderPadding);
437 :
438 : /*
439 : * Position aFrame and its descendants to their visual places. Also if aFrame
440 : * is not leaf, resize it to embrace its children.
441 : *
442 : * @param aFrame The frame which itself and its children are
443 : * going to be repositioned
444 : * @param aIsEvenLevel TRUE means the embedding level of this frame
445 : * is even (LTR)
446 : * @param aStartOrEnd The distance to the start or the end of aFrame
447 : * without considering its inline margin. If the
448 : * container is reordering frames in reverse
449 : * direction, it's the distance to the end,
450 : * otherwise, it's the distance to the start.
451 : * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState
452 : * @return The isize aFrame takes, including margins.
453 : */
454 : static nscoord RepositionFrame(nsIFrame* aFrame,
455 : bool aIsEvenLevel,
456 : nscoord aStartOrEnd,
457 : const nsContinuationStates* aContinuationStates,
458 : mozilla::WritingMode aContainerWM,
459 : bool aContainerReverseOrder,
460 : const nsSize& aContainerSize);
461 :
462 : /*
463 : * Initialize the continuation state(nsFrameContinuationState) to
464 : * (nullptr, 0) for aFrame and its descendants.
465 : *
466 : * @param aFrame The frame which itself and its descendants will
467 : * be initialized
468 : * @param aContinuationStates A map from nsIFrame* to nsFrameContinuationState
469 : */
470 : static void InitContinuationStates(nsIFrame* aFrame,
471 : nsContinuationStates* aContinuationStates);
472 :
473 : /*
474 : * Determine if aFrame is first or last, and set aIsFirst and
475 : * aIsLast values. Also set continuation states of
476 : * aContinuationStates.
477 : *
478 : * A frame is first if it's the first appearance of its continuation
479 : * chain on the line and the chain is on its first line.
480 : * A frame is last if it's the last appearance of its continuation
481 : * chain on the line and the chain is on its last line.
482 : *
483 : * N.B: "First appearance" and "Last appearance" in the previous
484 : * paragraph refer to the frame's inline direction, not necessarily
485 : * the line's.
486 : *
487 : * @param aContinuationStates A map from nsIFrame* to
488 : * nsFrameContinuationState
489 : * @param[in] aSpanDirMatchesLineDir TRUE means that the inline
490 : * direction of aFrame is the same
491 : * as its container
492 : * @param[out] aIsFirst TRUE means aFrame is first frame
493 : * or continuation
494 : * @param[out] aIsLast TRUE means aFrame is last frame
495 : * or continuation
496 : */
497 : static void IsFirstOrLast(nsIFrame* aFrame,
498 : const nsContinuationStates* aContinuationStates,
499 : bool aSpanInLineOrder /* in */,
500 : bool& aIsFirst /* out */,
501 : bool& aIsLast /* out */);
502 :
503 : /**
504 : * Adjust frame positions following their visual order
505 : *
506 : * @param aFirstChild the first kid
507 : * @return total inline size
508 : *
509 : * @lina 04/11/2000
510 : */
511 : static nscoord RepositionInlineFrames(BidiLineData* aBld,
512 : mozilla::WritingMode aLineWM,
513 : const nsSize& aContainerSize,
514 : nscoord aStart);
515 :
516 : /**
517 : * Helper method for Resolve()
518 : * Truncate a text frame to the end of a single-directional run and possibly
519 : * create a continuation frame for the remainder of its content.
520 : *
521 : * @param aFrame the original frame
522 : * @param aNewFrame [OUT] the new frame that was created
523 : * @param aStart [IN] the start of the content mapped by aFrame (and
524 : * any fluid continuations)
525 : * @param aEnd [IN] the offset of the end of the single-directional
526 : * text run.
527 : * @see Resolve()
528 : * @see RemoveBidiContinuation()
529 : */
530 : static inline
531 : nsresult EnsureBidiContinuation(nsIFrame* aFrame,
532 : nsIFrame** aNewFrame,
533 : int32_t aStart,
534 : int32_t aEnd);
535 :
536 : /**
537 : * Helper method for Resolve()
538 : * Convert one or more bidi continuation frames created in a previous reflow by
539 : * EnsureBidiContinuation() into fluid continuations.
540 : * @param aFrame the frame whose continuations are to be removed
541 : * @param aFirstIndex index of aFrame in mLogicalFrames
542 : * @param aLastIndex index of the last frame to be removed
543 : *
544 : * @see Resolve()
545 : * @see EnsureBidiContinuation()
546 : */
547 : static void RemoveBidiContinuation(BidiParagraphData* aBpd,
548 : nsIFrame* aFrame,
549 : int32_t aFirstIndex,
550 : int32_t aLastIndex);
551 : static void CalculateCharType(nsBidi* aBidiEngine,
552 : const char16_t* aText,
553 : int32_t& aOffset,
554 : int32_t aCharTypeLimit,
555 : int32_t& aRunLimit,
556 : int32_t& aRunLength,
557 : int32_t& aRunCount,
558 : uint8_t& aCharType,
559 : uint8_t& aPrevCharType);
560 :
561 : static void StripBidiControlCharacters(char16_t* aText,
562 : int32_t& aTextLength);
563 : };
564 :
565 : #endif /* nsBidiPresUtils_h___ */
|