Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "gfxDWriteTextAnalysis.h"
8 TextAnalysis::TextAnalysis(const wchar_t* text,
9 UINT32 textLength,
10 const wchar_t* localeName,
11 DWRITE_READING_DIRECTION readingDirection)
12 : mText(text)
13 , mTextLength(textLength)
14 , mLocaleName(localeName)
15 , mReadingDirection(readingDirection)
16 , mCurrentRun(nullptr)
17 {
18 }
20 TextAnalysis::~TextAnalysis()
21 {
22 // delete runs, except mRunHead which is part of the TextAnalysis object
23 for (Run *run = mRunHead.nextRun; run;) {
24 Run *origRun = run;
25 run = run->nextRun;
26 delete origRun;
27 }
28 }
30 STDMETHODIMP
31 TextAnalysis::GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
32 OUT Run **runHead)
33 {
34 // Analyzes the text using the script analyzer and returns
35 // the result as a series of runs.
37 HRESULT hr = S_OK;
39 // Initially start out with one result that covers the entire range.
40 // This result will be subdivided by the analysis processes.
41 mRunHead.mTextStart = 0;
42 mRunHead.mTextLength = mTextLength;
43 mRunHead.mBidiLevel =
44 (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
45 mRunHead.nextRun = nullptr;
46 mCurrentRun = &mRunHead;
48 // Call each of the analyzers in sequence, recording their results.
49 if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this,
50 0,
51 mTextLength,
52 this))) {
53 *runHead = &mRunHead;
54 }
56 return hr;
57 }
60 ////////////////////////////////////////////////////////////////////////////////
61 // IDWriteTextAnalysisSource source implementation
63 IFACEMETHODIMP
64 TextAnalysis::GetTextAtPosition(UINT32 textPosition,
65 OUT WCHAR const** textString,
66 OUT UINT32* textLength)
67 {
68 if (textPosition >= mTextLength) {
69 // No text at this position, valid query though.
70 *textString = nullptr;
71 *textLength = 0;
72 } else {
73 *textString = mText + textPosition;
74 *textLength = mTextLength - textPosition;
75 }
76 return S_OK;
77 }
80 IFACEMETHODIMP
81 TextAnalysis::GetTextBeforePosition(UINT32 textPosition,
82 OUT WCHAR const** textString,
83 OUT UINT32* textLength)
84 {
85 if (textPosition == 0 || textPosition > mTextLength) {
86 // Either there is no text before here (== 0), or this
87 // is an invalid position. The query is considered valid thouh.
88 *textString = nullptr;
89 *textLength = 0;
90 } else {
91 *textString = mText;
92 *textLength = textPosition;
93 }
94 return S_OK;
95 }
98 DWRITE_READING_DIRECTION STDMETHODCALLTYPE
99 TextAnalysis::GetParagraphReadingDirection()
100 {
101 // We support only a single reading direction.
102 return mReadingDirection;
103 }
106 IFACEMETHODIMP
107 TextAnalysis::GetLocaleName(UINT32 textPosition,
108 OUT UINT32* textLength,
109 OUT WCHAR const** localeName)
110 {
111 // Single locale name is used, valid until the end of the string.
112 *localeName = mLocaleName;
113 *textLength = mTextLength - textPosition;
115 return S_OK;
116 }
119 IFACEMETHODIMP
120 TextAnalysis::GetNumberSubstitution(UINT32 textPosition,
121 OUT UINT32* textLength,
122 OUT IDWriteNumberSubstitution** numberSubstitution)
123 {
124 // We do not support number substitution.
125 *numberSubstitution = nullptr;
126 *textLength = mTextLength - textPosition;
128 return S_OK;
129 }
132 ////////////////////////////////////////////////////////////////////////////////
133 // IDWriteTextAnalysisSink implementation
135 IFACEMETHODIMP
136 TextAnalysis::SetLineBreakpoints(UINT32 textPosition,
137 UINT32 textLength,
138 DWRITE_LINE_BREAKPOINT const* lineBreakpoints)
139 {
140 // We don't use this for now.
141 return S_OK;
142 }
145 IFACEMETHODIMP
146 TextAnalysis::SetScriptAnalysis(UINT32 textPosition,
147 UINT32 textLength,
148 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
149 {
150 SetCurrentRun(textPosition);
151 SplitCurrentRun(textPosition);
152 while (textLength > 0) {
153 Run *run = FetchNextRun(&textLength);
154 run->mScript = *scriptAnalysis;
155 }
157 return S_OK;
158 }
161 IFACEMETHODIMP
162 TextAnalysis::SetBidiLevel(UINT32 textPosition,
163 UINT32 textLength,
164 UINT8 explicitLevel,
165 UINT8 resolvedLevel)
166 {
167 // We don't use this for now.
168 return S_OK;
169 }
172 IFACEMETHODIMP
173 TextAnalysis::SetNumberSubstitution(UINT32 textPosition,
174 UINT32 textLength,
175 IDWriteNumberSubstitution* numberSubstitution)
176 {
177 // We don't use this for now.
178 return S_OK;
179 }
182 ////////////////////////////////////////////////////////////////////////////////
183 // Run modification.
185 TextAnalysis::Run *
186 TextAnalysis::FetchNextRun(IN OUT UINT32* textLength)
187 {
188 // Used by the sink setters, this returns a reference to the next run.
189 // Position and length are adjusted to now point after the current run
190 // being returned.
192 Run *origRun = mCurrentRun;
193 // Split the tail if needed (the length remaining is less than the
194 // current run's size).
195 if (*textLength < mCurrentRun->mTextLength) {
196 SplitCurrentRun(mCurrentRun->mTextStart + *textLength);
197 } else {
198 // Just advance the current run.
199 mCurrentRun = mCurrentRun->nextRun;
200 }
201 *textLength -= origRun->mTextLength;
203 // Return a reference to the run that was just current.
204 return origRun;
205 }
208 void TextAnalysis::SetCurrentRun(UINT32 textPosition)
209 {
210 // Move the current run to the given position.
211 // Since the analyzers generally return results in a forward manner,
212 // this will usually just return early. If not, find the
213 // corresponding run for the text position.
215 if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
216 return;
217 }
219 for (Run *run = &mRunHead; run; run = run->nextRun) {
220 if (run->ContainsTextPosition(textPosition)) {
221 mCurrentRun = run;
222 return;
223 }
224 }
225 NS_NOTREACHED("We should always be able to find the text position in one \
226 of our runs");
227 }
230 void TextAnalysis::SplitCurrentRun(UINT32 splitPosition)
231 {
232 if (!mCurrentRun) {
233 NS_ASSERTION(false, "SplitCurrentRun called without current run.");
234 // Shouldn't be calling this when no current run is set!
235 return;
236 }
237 // Split the current run.
238 if (splitPosition <= mCurrentRun->mTextStart) {
239 // No need to split, already the start of a run
240 // or before it. Usually the first.
241 return;
242 }
243 Run *newRun = new Run;
245 *newRun = *mCurrentRun;
247 // Insert the new run in our linked list.
248 newRun->nextRun = mCurrentRun->nextRun;
249 mCurrentRun->nextRun = newRun;
251 // Adjust runs' text positions and lengths.
252 UINT32 splitPoint = splitPosition - mCurrentRun->mTextStart;
253 newRun->mTextStart += splitPoint;
254 newRun->mTextLength -= splitPoint;
255 mCurrentRun->mTextLength = splitPoint;
256 mCurrentRun = newRun;
257 }