gfx/thebes/gfxDWriteTextAnalysis.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial