1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsTextFrameUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,268 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTextFrameUtils.h" 1.10 + 1.11 +#include "nsUnicharUtils.h" 1.12 +#include "nsBidiUtils.h" 1.13 +#include "nsIContent.h" 1.14 +#include "nsStyleStruct.h" 1.15 +#include "nsTextFragment.h" 1.16 +#include <algorithm> 1.17 + 1.18 +static bool IsDiscardable(char16_t ch, uint32_t* aFlags) 1.19 +{ 1.20 + // Unlike IS_DISCARDABLE, we don't discard \r. \r will be ignored by gfxTextRun 1.21 + // and discarding it would force us to copy text in many cases of preformatted 1.22 + // text containing \r\n. 1.23 + if (ch == CH_SHY) { 1.24 + *aFlags |= nsTextFrameUtils::TEXT_HAS_SHY; 1.25 + return true; 1.26 + } 1.27 + return IsBidiControl(ch); 1.28 +} 1.29 + 1.30 +static bool IsDiscardable(uint8_t ch, uint32_t* aFlags) 1.31 +{ 1.32 + if (ch == CH_SHY) { 1.33 + *aFlags |= nsTextFrameUtils::TEXT_HAS_SHY; 1.34 + return true; 1.35 + } 1.36 + return false; 1.37 +} 1.38 + 1.39 +char16_t* 1.40 +nsTextFrameUtils::TransformText(const char16_t* aText, uint32_t aLength, 1.41 + char16_t* aOutput, 1.42 + CompressionMode aCompression, 1.43 + uint8_t* aIncomingFlags, 1.44 + gfxSkipChars* aSkipChars, 1.45 + uint32_t* aAnalysisFlags) 1.46 +{ 1.47 + uint32_t flags = 0; 1.48 + char16_t* outputStart = aOutput; 1.49 + 1.50 + bool lastCharArabic = false; 1.51 + 1.52 + if (aCompression == COMPRESS_NONE || 1.53 + aCompression == DISCARD_NEWLINE) { 1.54 + // Skip discardables. 1.55 + uint32_t i; 1.56 + for (i = 0; i < aLength; ++i) { 1.57 + char16_t ch = *aText++; 1.58 + if (IsDiscardable(ch, &flags) || 1.59 + (ch == '\n' && aCompression == DISCARD_NEWLINE)) { 1.60 + aSkipChars->SkipChar(); 1.61 + } else { 1.62 + aSkipChars->KeepChar(); 1.63 + if (ch > ' ') { 1.64 + lastCharArabic = IS_ARABIC_CHAR(ch); 1.65 + } else if (ch == '\t') { 1.66 + flags |= TEXT_HAS_TAB; 1.67 + } 1.68 + *aOutput++ = ch; 1.69 + } 1.70 + } 1.71 + if (lastCharArabic) { 1.72 + *aIncomingFlags |= INCOMING_ARABICCHAR; 1.73 + } else { 1.74 + *aIncomingFlags &= ~INCOMING_ARABICCHAR; 1.75 + } 1.76 + *aIncomingFlags &= ~INCOMING_WHITESPACE; 1.77 + } else { 1.78 + bool inWhitespace = (*aIncomingFlags & INCOMING_WHITESPACE) != 0; 1.79 + uint32_t i; 1.80 + for (i = 0; i < aLength; ++i) { 1.81 + char16_t ch = *aText++; 1.82 + bool nowInWhitespace; 1.83 + if (ch == ' ' && 1.84 + (i + 1 >= aLength || 1.85 + !IsSpaceCombiningSequenceTail(aText, aLength - (i + 1)))) { 1.86 + nowInWhitespace = true; 1.87 + } else if (ch == '\n' && aCompression == COMPRESS_WHITESPACE_NEWLINE) { 1.88 + if (i > 0 && IS_CJ_CHAR(aText[-1]) && 1.89 + i + 1 < aLength && IS_CJ_CHAR(aText[1])) { 1.90 + // Discard newlines between CJK chars. 1.91 + // XXX this really requires more context to get right! 1.92 + aSkipChars->SkipChar(); 1.93 + continue; 1.94 + } 1.95 + nowInWhitespace = true; 1.96 + } else { 1.97 + nowInWhitespace = ch == '\t'; 1.98 + } 1.99 + 1.100 + if (!nowInWhitespace) { 1.101 + if (IsDiscardable(ch, &flags)) { 1.102 + aSkipChars->SkipChar(); 1.103 + nowInWhitespace = inWhitespace; 1.104 + } else { 1.105 + *aOutput++ = ch; 1.106 + aSkipChars->KeepChar(); 1.107 + lastCharArabic = IS_ARABIC_CHAR(ch); 1.108 + } 1.109 + } else { 1.110 + if (inWhitespace) { 1.111 + aSkipChars->SkipChar(); 1.112 + } else { 1.113 + if (ch != ' ') { 1.114 + flags |= TEXT_WAS_TRANSFORMED; 1.115 + } 1.116 + *aOutput++ = ' '; 1.117 + aSkipChars->KeepChar(); 1.118 + } 1.119 + } 1.120 + inWhitespace = nowInWhitespace; 1.121 + } 1.122 + if (lastCharArabic) { 1.123 + *aIncomingFlags |= INCOMING_ARABICCHAR; 1.124 + } else { 1.125 + *aIncomingFlags &= ~INCOMING_ARABICCHAR; 1.126 + } 1.127 + if (inWhitespace) { 1.128 + *aIncomingFlags |= INCOMING_WHITESPACE; 1.129 + } else { 1.130 + *aIncomingFlags &= ~INCOMING_WHITESPACE; 1.131 + } 1.132 + } 1.133 + 1.134 + if (outputStart + aLength != aOutput) { 1.135 + flags |= TEXT_WAS_TRANSFORMED; 1.136 + } 1.137 + *aAnalysisFlags = flags; 1.138 + return aOutput; 1.139 +} 1.140 + 1.141 +uint8_t* 1.142 +nsTextFrameUtils::TransformText(const uint8_t* aText, uint32_t aLength, 1.143 + uint8_t* aOutput, 1.144 + CompressionMode aCompression, 1.145 + uint8_t* aIncomingFlags, 1.146 + gfxSkipChars* aSkipChars, 1.147 + uint32_t* aAnalysisFlags) 1.148 +{ 1.149 + uint32_t flags = 0; 1.150 + uint8_t* outputStart = aOutput; 1.151 + 1.152 + if (aCompression == COMPRESS_NONE || 1.153 + aCompression == DISCARD_NEWLINE) { 1.154 + // Skip discardables. 1.155 + uint32_t i; 1.156 + for (i = 0; i < aLength; ++i) { 1.157 + uint8_t ch = *aText++; 1.158 + if (IsDiscardable(ch, &flags) || 1.159 + (ch == '\n' && aCompression == DISCARD_NEWLINE)) { 1.160 + aSkipChars->SkipChar(); 1.161 + } else { 1.162 + aSkipChars->KeepChar(); 1.163 + if (ch == '\t') { 1.164 + flags |= TEXT_HAS_TAB; 1.165 + } 1.166 + *aOutput++ = ch; 1.167 + } 1.168 + } 1.169 + *aIncomingFlags &= ~(INCOMING_ARABICCHAR | INCOMING_WHITESPACE); 1.170 + } else { 1.171 + bool inWhitespace = (*aIncomingFlags & INCOMING_WHITESPACE) != 0; 1.172 + uint32_t i; 1.173 + for (i = 0; i < aLength; ++i) { 1.174 + uint8_t ch = *aText++; 1.175 + bool nowInWhitespace = ch == ' ' || ch == '\t' || 1.176 + (ch == '\n' && aCompression == COMPRESS_WHITESPACE_NEWLINE); 1.177 + if (!nowInWhitespace) { 1.178 + if (IsDiscardable(ch, &flags)) { 1.179 + aSkipChars->SkipChar(); 1.180 + nowInWhitespace = inWhitespace; 1.181 + } else { 1.182 + *aOutput++ = ch; 1.183 + aSkipChars->KeepChar(); 1.184 + } 1.185 + } else { 1.186 + if (inWhitespace) { 1.187 + aSkipChars->SkipChar(); 1.188 + } else { 1.189 + if (ch != ' ') { 1.190 + flags |= TEXT_WAS_TRANSFORMED; 1.191 + } 1.192 + *aOutput++ = ' '; 1.193 + aSkipChars->KeepChar(); 1.194 + } 1.195 + } 1.196 + inWhitespace = nowInWhitespace; 1.197 + } 1.198 + *aIncomingFlags &= ~INCOMING_ARABICCHAR; 1.199 + if (inWhitespace) { 1.200 + *aIncomingFlags |= INCOMING_WHITESPACE; 1.201 + } else { 1.202 + *aIncomingFlags &= ~INCOMING_WHITESPACE; 1.203 + } 1.204 + } 1.205 + 1.206 + if (outputStart + aLength != aOutput) { 1.207 + flags |= TEXT_WAS_TRANSFORMED; 1.208 + } 1.209 + *aAnalysisFlags = flags; 1.210 + return aOutput; 1.211 +} 1.212 + 1.213 +uint32_t 1.214 +nsTextFrameUtils::ComputeApproximateLengthWithWhitespaceCompression( 1.215 + nsIContent *aContent, const nsStyleText *aStyleText) 1.216 +{ 1.217 + const nsTextFragment *frag = aContent->GetText(); 1.218 + // This is an approximation so we don't really need anything 1.219 + // too fancy here. 1.220 + uint32_t len; 1.221 + if (aStyleText->WhiteSpaceIsSignificant()) { 1.222 + len = frag->GetLength(); 1.223 + } else { 1.224 + bool is2b = frag->Is2b(); 1.225 + union { 1.226 + const char *s1b; 1.227 + const char16_t *s2b; 1.228 + } u; 1.229 + if (is2b) { 1.230 + u.s2b = frag->Get2b(); 1.231 + } else { 1.232 + u.s1b = frag->Get1b(); 1.233 + } 1.234 + bool prevWS = true; // more important to ignore blocks with 1.235 + // only whitespace than get inline boundaries 1.236 + // exactly right 1.237 + len = 0; 1.238 + for (uint32_t i = 0, i_end = frag->GetLength(); i < i_end; ++i) { 1.239 + char16_t c = is2b ? u.s2b[i] : u.s1b[i]; 1.240 + if (c == ' ' || c == '\n' || c == '\t' || c == '\r') { 1.241 + if (!prevWS) { 1.242 + ++len; 1.243 + } 1.244 + prevWS = true; 1.245 + } else { 1.246 + ++len; 1.247 + prevWS = false; 1.248 + } 1.249 + } 1.250 + } 1.251 + return len; 1.252 +} 1.253 + 1.254 +bool nsSkipCharsRunIterator::NextRun() { 1.255 + do { 1.256 + if (mRunLength) { 1.257 + mIterator.AdvanceOriginal(mRunLength); 1.258 + NS_ASSERTION(mRunLength > 0, "No characters in run (initial length too large?)"); 1.259 + if (!mSkipped || mLengthIncludesSkipped) { 1.260 + mRemainingLength -= mRunLength; 1.261 + } 1.262 + } 1.263 + if (!mRemainingLength) 1.264 + return false; 1.265 + int32_t length; 1.266 + mSkipped = mIterator.IsOriginalCharSkipped(&length); 1.267 + mRunLength = std::min(length, mRemainingLength); 1.268 + } while (!mVisitSkipped && mSkipped); 1.269 + 1.270 + return true; 1.271 +}