1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxDWriteTextAnalysis.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,257 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 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 "gfxDWriteTextAnalysis.h" 1.10 + 1.11 +TextAnalysis::TextAnalysis(const wchar_t* text, 1.12 + UINT32 textLength, 1.13 + const wchar_t* localeName, 1.14 + DWRITE_READING_DIRECTION readingDirection) 1.15 + : mText(text) 1.16 + , mTextLength(textLength) 1.17 + , mLocaleName(localeName) 1.18 + , mReadingDirection(readingDirection) 1.19 + , mCurrentRun(nullptr) 1.20 +{ 1.21 +} 1.22 + 1.23 +TextAnalysis::~TextAnalysis() 1.24 +{ 1.25 + // delete runs, except mRunHead which is part of the TextAnalysis object 1.26 + for (Run *run = mRunHead.nextRun; run;) { 1.27 + Run *origRun = run; 1.28 + run = run->nextRun; 1.29 + delete origRun; 1.30 + } 1.31 +} 1.32 + 1.33 +STDMETHODIMP 1.34 +TextAnalysis::GenerateResults(IDWriteTextAnalyzer* textAnalyzer, 1.35 + OUT Run **runHead) 1.36 +{ 1.37 + // Analyzes the text using the script analyzer and returns 1.38 + // the result as a series of runs. 1.39 + 1.40 + HRESULT hr = S_OK; 1.41 + 1.42 + // Initially start out with one result that covers the entire range. 1.43 + // This result will be subdivided by the analysis processes. 1.44 + mRunHead.mTextStart = 0; 1.45 + mRunHead.mTextLength = mTextLength; 1.46 + mRunHead.mBidiLevel = 1.47 + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); 1.48 + mRunHead.nextRun = nullptr; 1.49 + mCurrentRun = &mRunHead; 1.50 + 1.51 + // Call each of the analyzers in sequence, recording their results. 1.52 + if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this, 1.53 + 0, 1.54 + mTextLength, 1.55 + this))) { 1.56 + *runHead = &mRunHead; 1.57 + } 1.58 + 1.59 + return hr; 1.60 +} 1.61 + 1.62 + 1.63 +//////////////////////////////////////////////////////////////////////////////// 1.64 +// IDWriteTextAnalysisSource source implementation 1.65 + 1.66 +IFACEMETHODIMP 1.67 +TextAnalysis::GetTextAtPosition(UINT32 textPosition, 1.68 + OUT WCHAR const** textString, 1.69 + OUT UINT32* textLength) 1.70 +{ 1.71 + if (textPosition >= mTextLength) { 1.72 + // No text at this position, valid query though. 1.73 + *textString = nullptr; 1.74 + *textLength = 0; 1.75 + } else { 1.76 + *textString = mText + textPosition; 1.77 + *textLength = mTextLength - textPosition; 1.78 + } 1.79 + return S_OK; 1.80 +} 1.81 + 1.82 + 1.83 +IFACEMETHODIMP 1.84 +TextAnalysis::GetTextBeforePosition(UINT32 textPosition, 1.85 + OUT WCHAR const** textString, 1.86 + OUT UINT32* textLength) 1.87 +{ 1.88 + if (textPosition == 0 || textPosition > mTextLength) { 1.89 + // Either there is no text before here (== 0), or this 1.90 + // is an invalid position. The query is considered valid thouh. 1.91 + *textString = nullptr; 1.92 + *textLength = 0; 1.93 + } else { 1.94 + *textString = mText; 1.95 + *textLength = textPosition; 1.96 + } 1.97 + return S_OK; 1.98 +} 1.99 + 1.100 + 1.101 +DWRITE_READING_DIRECTION STDMETHODCALLTYPE 1.102 +TextAnalysis::GetParagraphReadingDirection() 1.103 +{ 1.104 + // We support only a single reading direction. 1.105 + return mReadingDirection; 1.106 +} 1.107 + 1.108 + 1.109 +IFACEMETHODIMP 1.110 +TextAnalysis::GetLocaleName(UINT32 textPosition, 1.111 + OUT UINT32* textLength, 1.112 + OUT WCHAR const** localeName) 1.113 +{ 1.114 + // Single locale name is used, valid until the end of the string. 1.115 + *localeName = mLocaleName; 1.116 + *textLength = mTextLength - textPosition; 1.117 + 1.118 + return S_OK; 1.119 +} 1.120 + 1.121 + 1.122 +IFACEMETHODIMP 1.123 +TextAnalysis::GetNumberSubstitution(UINT32 textPosition, 1.124 + OUT UINT32* textLength, 1.125 + OUT IDWriteNumberSubstitution** numberSubstitution) 1.126 +{ 1.127 + // We do not support number substitution. 1.128 + *numberSubstitution = nullptr; 1.129 + *textLength = mTextLength - textPosition; 1.130 + 1.131 + return S_OK; 1.132 +} 1.133 + 1.134 + 1.135 +//////////////////////////////////////////////////////////////////////////////// 1.136 +// IDWriteTextAnalysisSink implementation 1.137 + 1.138 +IFACEMETHODIMP 1.139 +TextAnalysis::SetLineBreakpoints(UINT32 textPosition, 1.140 + UINT32 textLength, 1.141 + DWRITE_LINE_BREAKPOINT const* lineBreakpoints) 1.142 +{ 1.143 + // We don't use this for now. 1.144 + return S_OK; 1.145 +} 1.146 + 1.147 + 1.148 +IFACEMETHODIMP 1.149 +TextAnalysis::SetScriptAnalysis(UINT32 textPosition, 1.150 + UINT32 textLength, 1.151 + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) 1.152 +{ 1.153 + SetCurrentRun(textPosition); 1.154 + SplitCurrentRun(textPosition); 1.155 + while (textLength > 0) { 1.156 + Run *run = FetchNextRun(&textLength); 1.157 + run->mScript = *scriptAnalysis; 1.158 + } 1.159 + 1.160 + return S_OK; 1.161 +} 1.162 + 1.163 + 1.164 +IFACEMETHODIMP 1.165 +TextAnalysis::SetBidiLevel(UINT32 textPosition, 1.166 + UINT32 textLength, 1.167 + UINT8 explicitLevel, 1.168 + UINT8 resolvedLevel) 1.169 +{ 1.170 + // We don't use this for now. 1.171 + return S_OK; 1.172 +} 1.173 + 1.174 + 1.175 +IFACEMETHODIMP 1.176 +TextAnalysis::SetNumberSubstitution(UINT32 textPosition, 1.177 + UINT32 textLength, 1.178 + IDWriteNumberSubstitution* numberSubstitution) 1.179 +{ 1.180 + // We don't use this for now. 1.181 + return S_OK; 1.182 +} 1.183 + 1.184 + 1.185 +//////////////////////////////////////////////////////////////////////////////// 1.186 +// Run modification. 1.187 + 1.188 +TextAnalysis::Run * 1.189 +TextAnalysis::FetchNextRun(IN OUT UINT32* textLength) 1.190 +{ 1.191 + // Used by the sink setters, this returns a reference to the next run. 1.192 + // Position and length are adjusted to now point after the current run 1.193 + // being returned. 1.194 + 1.195 + Run *origRun = mCurrentRun; 1.196 + // Split the tail if needed (the length remaining is less than the 1.197 + // current run's size). 1.198 + if (*textLength < mCurrentRun->mTextLength) { 1.199 + SplitCurrentRun(mCurrentRun->mTextStart + *textLength); 1.200 + } else { 1.201 + // Just advance the current run. 1.202 + mCurrentRun = mCurrentRun->nextRun; 1.203 + } 1.204 + *textLength -= origRun->mTextLength; 1.205 + 1.206 + // Return a reference to the run that was just current. 1.207 + return origRun; 1.208 +} 1.209 + 1.210 + 1.211 +void TextAnalysis::SetCurrentRun(UINT32 textPosition) 1.212 +{ 1.213 + // Move the current run to the given position. 1.214 + // Since the analyzers generally return results in a forward manner, 1.215 + // this will usually just return early. If not, find the 1.216 + // corresponding run for the text position. 1.217 + 1.218 + if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) { 1.219 + return; 1.220 + } 1.221 + 1.222 + for (Run *run = &mRunHead; run; run = run->nextRun) { 1.223 + if (run->ContainsTextPosition(textPosition)) { 1.224 + mCurrentRun = run; 1.225 + return; 1.226 + } 1.227 + } 1.228 + NS_NOTREACHED("We should always be able to find the text position in one \ 1.229 + of our runs"); 1.230 +} 1.231 + 1.232 + 1.233 +void TextAnalysis::SplitCurrentRun(UINT32 splitPosition) 1.234 +{ 1.235 + if (!mCurrentRun) { 1.236 + NS_ASSERTION(false, "SplitCurrentRun called without current run."); 1.237 + // Shouldn't be calling this when no current run is set! 1.238 + return; 1.239 + } 1.240 + // Split the current run. 1.241 + if (splitPosition <= mCurrentRun->mTextStart) { 1.242 + // No need to split, already the start of a run 1.243 + // or before it. Usually the first. 1.244 + return; 1.245 + } 1.246 + Run *newRun = new Run; 1.247 + 1.248 + *newRun = *mCurrentRun; 1.249 + 1.250 + // Insert the new run in our linked list. 1.251 + newRun->nextRun = mCurrentRun->nextRun; 1.252 + mCurrentRun->nextRun = newRun; 1.253 + 1.254 + // Adjust runs' text positions and lengths. 1.255 + UINT32 splitPoint = splitPosition - mCurrentRun->mTextStart; 1.256 + newRun->mTextStart += splitPoint; 1.257 + newRun->mTextLength -= splitPoint; 1.258 + mCurrentRun->mTextLength = splitPoint; 1.259 + mCurrentRun = newRun; 1.260 +}