1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTextFrameUtils.h"
8 #include "nsUnicharUtils.h"
9 #include "nsBidiUtils.h"
10 #include "nsIContent.h"
11 #include "nsStyleStruct.h"
12 #include "nsTextFragment.h"
15 static bool IsDiscardable(char16_t ch
, uint32_t* aFlags
)
17 // Unlike IS_DISCARDABLE, we don't discard \r. \r will be ignored by gfxTextRun
18 // and discarding it would force us to copy text in many cases of preformatted
19 // text containing \r\n.
21 *aFlags
|= nsTextFrameUtils::TEXT_HAS_SHY
;
24 return IsBidiControl(ch
);
27 static bool IsDiscardable(uint8_t ch
, uint32_t* aFlags
)
30 *aFlags
|= nsTextFrameUtils::TEXT_HAS_SHY
;
37 nsTextFrameUtils::TransformText(const char16_t
* aText
, uint32_t aLength
,
39 CompressionMode aCompression
,
40 uint8_t* aIncomingFlags
,
41 gfxSkipChars
* aSkipChars
,
42 uint32_t* aAnalysisFlags
)
45 char16_t
* outputStart
= aOutput
;
47 bool lastCharArabic
= false;
49 if (aCompression
== COMPRESS_NONE
||
50 aCompression
== COMPRESS_NONE_TRANSFORM_TO_SPACE
) {
53 for (i
= 0; i
< aLength
; ++i
) {
54 char16_t ch
= *aText
++;
55 if (IsDiscardable(ch
, &flags
)) {
56 aSkipChars
->SkipChar();
58 aSkipChars
->KeepChar();
60 lastCharArabic
= IS_ARABIC_CHAR(ch
);
61 } else if (aCompression
== COMPRESS_NONE_TRANSFORM_TO_SPACE
) {
62 if (ch
== '\t' || ch
== '\n') {
64 flags
|= TEXT_WAS_TRANSFORMED
;
67 // aCompression == COMPRESS_NONE
69 flags
|= TEXT_HAS_TAB
;
76 *aIncomingFlags
|= INCOMING_ARABICCHAR
;
78 *aIncomingFlags
&= ~INCOMING_ARABICCHAR
;
80 *aIncomingFlags
&= ~INCOMING_WHITESPACE
;
82 bool inWhitespace
= (*aIncomingFlags
& INCOMING_WHITESPACE
) != 0;
84 for (i
= 0; i
< aLength
; ++i
) {
85 char16_t ch
= *aText
++;
89 !IsSpaceCombiningSequenceTail(aText
, aLength
- (i
+ 1)))) {
90 nowInWhitespace
= true;
91 } else if (ch
== '\n' && aCompression
== COMPRESS_WHITESPACE_NEWLINE
) {
92 if (i
> 0 && IS_CJ_CHAR(aText
[-1]) &&
93 i
+ 1 < aLength
&& IS_CJ_CHAR(aText
[1])) {
94 // Discard newlines between CJK chars.
95 // XXX this really requires more context to get right!
96 aSkipChars
->SkipChar();
99 nowInWhitespace
= true;
101 nowInWhitespace
= ch
== '\t';
104 if (!nowInWhitespace
) {
105 if (IsDiscardable(ch
, &flags
)) {
106 aSkipChars
->SkipChar();
107 nowInWhitespace
= inWhitespace
;
110 aSkipChars
->KeepChar();
111 lastCharArabic
= IS_ARABIC_CHAR(ch
);
115 aSkipChars
->SkipChar();
118 flags
|= TEXT_WAS_TRANSFORMED
;
121 aSkipChars
->KeepChar();
124 inWhitespace
= nowInWhitespace
;
126 if (lastCharArabic
) {
127 *aIncomingFlags
|= INCOMING_ARABICCHAR
;
129 *aIncomingFlags
&= ~INCOMING_ARABICCHAR
;
132 *aIncomingFlags
|= INCOMING_WHITESPACE
;
134 *aIncomingFlags
&= ~INCOMING_WHITESPACE
;
138 if (outputStart
+ aLength
!= aOutput
) {
139 flags
|= TEXT_WAS_TRANSFORMED
;
141 *aAnalysisFlags
= flags
;
146 nsTextFrameUtils::TransformText(const uint8_t* aText
, uint32_t aLength
,
148 CompressionMode aCompression
,
149 uint8_t* aIncomingFlags
,
150 gfxSkipChars
* aSkipChars
,
151 uint32_t* aAnalysisFlags
)
154 uint8_t* outputStart
= aOutput
;
156 if (aCompression
== COMPRESS_NONE
||
157 aCompression
== COMPRESS_NONE_TRANSFORM_TO_SPACE
) {
158 // Skip discardables.
160 for (i
= 0; i
< aLength
; ++i
) {
161 uint8_t ch
= *aText
++;
162 if (IsDiscardable(ch
, &flags
)) {
163 aSkipChars
->SkipChar();
165 aSkipChars
->KeepChar();
166 if (aCompression
== COMPRESS_NONE_TRANSFORM_TO_SPACE
) {
167 if (ch
== '\t' || ch
== '\n') {
169 flags
|= TEXT_WAS_TRANSFORMED
;
172 // aCompression == COMPRESS_NONE
174 flags
|= TEXT_HAS_TAB
;
180 *aIncomingFlags
&= ~(INCOMING_ARABICCHAR
| INCOMING_WHITESPACE
);
182 bool inWhitespace
= (*aIncomingFlags
& INCOMING_WHITESPACE
) != 0;
184 for (i
= 0; i
< aLength
; ++i
) {
185 uint8_t ch
= *aText
++;
186 bool nowInWhitespace
= ch
== ' ' || ch
== '\t' ||
187 (ch
== '\n' && aCompression
== COMPRESS_WHITESPACE_NEWLINE
);
188 if (!nowInWhitespace
) {
189 if (IsDiscardable(ch
, &flags
)) {
190 aSkipChars
->SkipChar();
191 nowInWhitespace
= inWhitespace
;
194 aSkipChars
->KeepChar();
198 aSkipChars
->SkipChar();
201 flags
|= TEXT_WAS_TRANSFORMED
;
204 aSkipChars
->KeepChar();
207 inWhitespace
= nowInWhitespace
;
209 *aIncomingFlags
&= ~INCOMING_ARABICCHAR
;
211 *aIncomingFlags
|= INCOMING_WHITESPACE
;
213 *aIncomingFlags
&= ~INCOMING_WHITESPACE
;
217 if (outputStart
+ aLength
!= aOutput
) {
218 flags
|= TEXT_WAS_TRANSFORMED
;
220 *aAnalysisFlags
= flags
;
225 nsTextFrameUtils::ComputeApproximateLengthWithWhitespaceCompression(
226 nsIContent
*aContent
, const nsStyleText
*aStyleText
)
228 const nsTextFragment
*frag
= aContent
->GetText();
229 // This is an approximation so we don't really need anything
232 if (aStyleText
->WhiteSpaceIsSignificant()) {
233 len
= frag
->GetLength();
235 bool is2b
= frag
->Is2b();
241 u
.s2b
= frag
->Get2b();
243 u
.s1b
= frag
->Get1b();
245 bool prevWS
= true; // more important to ignore blocks with
246 // only whitespace than get inline boundaries
249 for (uint32_t i
= 0, i_end
= frag
->GetLength(); i
< i_end
; ++i
) {
250 char16_t c
= is2b
? u
.s2b
[i
] : u
.s1b
[i
];
251 if (c
== ' ' || c
== '\n' || c
== '\t' || c
== '\r') {
265 bool nsSkipCharsRunIterator::NextRun() {
268 mIterator
.AdvanceOriginal(mRunLength
);
269 NS_ASSERTION(mRunLength
> 0, "No characters in run (initial length too large?)");
270 if (!mSkipped
|| mLengthIncludesSkipped
) {
271 mRemainingLength
-= mRunLength
;
274 if (!mRemainingLength
)
277 mSkipped
= mIterator
.IsOriginalCharSkipped(&length
);
278 mRunLength
= std::min(length
, mRemainingLength
);
279 } while (!mVisitSkipped
&& mSkipped
);