| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. |
| 3 * | 3 * |
| 4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
| 5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
| 6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
| 7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
| 8 * | 8 * |
| 9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
| 13 * | 13 * |
| 14 * You should have received a copy of the GNU Library General Public License | 14 * You should have received a copy of the GNU Library General Public License |
| 15 * along with this library; see the file COPYING.LIB. If not, write to | 15 * along with this library; see the file COPYING.LIB. If not, write to |
| 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
| 18 */ | 18 */ |
| 19 | 19 |
| 20 #include "config.h" | 20 #include "config.h" |
| 21 #include "core/layout/svg/SVGTextQuery.h" | 21 #include "core/layout/svg/SVGTextQuery.h" |
| 22 | 22 |
| 23 #include "core/layout/LayoutBlockFlow.h" | 23 #include "core/layout/LayoutBlockFlow.h" |
| 24 #include "core/layout/LayoutInline.h" | 24 #include "core/layout/LayoutInline.h" |
| 25 #include "core/layout/line/InlineFlowBox.h" | 25 #include "core/layout/line/InlineFlowBox.h" |
| 26 #include "core/layout/svg/LayoutSVGInlineText.h" | 26 #include "core/layout/svg/LayoutSVGInlineText.h" |
| 27 #include "core/layout/svg/SVGTextFragment.h" |
| 27 #include "core/layout/svg/SVGTextMetrics.h" | 28 #include "core/layout/svg/SVGTextMetrics.h" |
| 28 #include "core/layout/svg/line/SVGInlineTextBox.h" | 29 #include "core/layout/svg/line/SVGInlineTextBox.h" |
| 29 #include "platform/FloatConversion.h" | 30 #include "platform/FloatConversion.h" |
| 30 #include "wtf/MathExtras.h" | 31 #include "wtf/MathExtras.h" |
| 32 #include "wtf/Vector.h" |
| 31 | 33 |
| 32 namespace blink { | 34 namespace blink { |
| 33 | 35 |
| 34 // Base structure for callback user data | 36 // Base structure for callback user data |
| 35 struct SVGTextQuery::Data { | 37 struct QueryData { |
| 36 Data() | 38 QueryData() |
| 37 : isVerticalText(false) | 39 : isVerticalText(false) |
| 38 , processedCharacters(0) | 40 , processedCharacters(0) |
| 39 , textLayoutObject(0) | 41 , textLayoutObject(0) |
| 40 , textBox(0) | 42 , textBox(0) |
| 41 { | 43 { |
| 42 } | 44 } |
| 43 | 45 |
| 44 bool isVerticalText; | 46 bool isVerticalText; |
| 45 unsigned processedCharacters; | 47 unsigned processedCharacters; |
| 46 LayoutSVGInlineText* textLayoutObject; | 48 LayoutSVGInlineText* textLayoutObject; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 70 // LayoutSVGInline only ever contains a single line box. | 72 // LayoutSVGInline only ever contains a single line box. |
| 71 InlineFlowBox* flowBox = layoutInline->firstLineBox(); | 73 InlineFlowBox* flowBox = layoutInline->firstLineBox(); |
| 72 ASSERT(flowBox == layoutInline->lastLineBox()); | 74 ASSERT(flowBox == layoutInline->lastLineBox()); |
| 73 return flowBox; | 75 return flowBox; |
| 74 } | 76 } |
| 75 | 77 |
| 76 ASSERT_NOT_REACHED(); | 78 ASSERT_NOT_REACHED(); |
| 77 return 0; | 79 return 0; |
| 78 } | 80 } |
| 79 | 81 |
| 80 SVGTextQuery::SVGTextQuery(LayoutObject* layoutObject) | 82 static void collectTextBoxesInFlowBox(InlineFlowBox* flowBox, Vector<SVGInlineTe
xtBox*>& textBoxes) |
| 81 { | |
| 82 collectTextBoxesInFlowBox(flowBoxForLayoutObject(layoutObject)); | |
| 83 } | |
| 84 | |
| 85 void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox) | |
| 86 { | 83 { |
| 87 if (!flowBox) | 84 if (!flowBox) |
| 88 return; | 85 return; |
| 89 | 86 |
| 90 for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnL
ine()) { | 87 for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnL
ine()) { |
| 91 if (child->isInlineFlowBox()) { | 88 if (child->isInlineFlowBox()) { |
| 92 // Skip generated content. | 89 // Skip generated content. |
| 93 if (!child->layoutObject().node()) | 90 if (!child->layoutObject().node()) |
| 94 continue; | 91 continue; |
| 95 | 92 |
| 96 collectTextBoxesInFlowBox(toInlineFlowBox(child)); | 93 collectTextBoxesInFlowBox(toInlineFlowBox(child), textBoxes); |
| 97 continue; | 94 continue; |
| 98 } | 95 } |
| 99 | 96 |
| 100 if (child->isSVGInlineTextBox()) | 97 if (child->isSVGInlineTextBox()) |
| 101 m_textBoxes.append(toSVGInlineTextBox(child)); | 98 textBoxes.append(toSVGInlineTextBox(child)); |
| 102 } | 99 } |
| 103 } | 100 } |
| 104 | 101 |
| 105 bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextFragmentCallback fra
gmentCallback) const | 102 typedef bool ProcessTextFragmentCallback(QueryData*, const SVGTextFragment&); |
| 103 |
| 104 static bool executeQuery(LayoutObject* queryRoot, QueryData* queryData, ProcessT
extFragmentCallback fragmentCallback) |
| 106 { | 105 { |
| 106 Vector<SVGInlineTextBox*> textBoxes; |
| 107 collectTextBoxesInFlowBox(flowBoxForLayoutObject(queryRoot), textBoxes); |
| 108 |
| 107 unsigned processedCharacters = 0; | 109 unsigned processedCharacters = 0; |
| 108 unsigned textBoxCount = m_textBoxes.size(); | 110 unsigned textBoxCount = textBoxes.size(); |
| 109 | 111 |
| 110 // Loop over all text boxes | 112 // Loop over all text boxes |
| 111 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBox
Position) { | 113 for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBox
Position) { |
| 112 queryData->textBox = m_textBoxes.at(textBoxPosition); | 114 queryData->textBox = textBoxes[textBoxPosition]; |
| 113 queryData->textLayoutObject = &toLayoutSVGInlineText(queryData->textBox-
>layoutObject()); | 115 queryData->textLayoutObject = &toLayoutSVGInlineText(queryData->textBox-
>layoutObject()); |
| 114 ASSERT(queryData->textLayoutObject->style()); | 116 ASSERT(queryData->textLayoutObject->style()); |
| 115 | 117 |
| 116 queryData->isVerticalText = queryData->textLayoutObject->style()->svgSty
le().isVerticalWritingMode(); | 118 queryData->isVerticalText = queryData->textLayoutObject->style()->svgSty
le().isVerticalWritingMode(); |
| 117 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragm
ents(); | 119 const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragm
ents(); |
| 118 | 120 |
| 119 // Loop over all text fragments in this text box, firing a callback for
each. | 121 // Loop over all text fragments in this text box, firing a callback for
each. |
| 120 unsigned fragmentCount = fragments.size(); | 122 unsigned fragmentCount = fragments.size(); |
| 121 for (unsigned i = 0; i < fragmentCount; ++i) { | 123 for (unsigned i = 0; i < fragmentCount; ++i) { |
| 122 const SVGTextFragment& fragment = fragments.at(i); | 124 const SVGTextFragment& fragment = fragments.at(i); |
| 123 if ((this->*fragmentCallback)(queryData, fragment)) | 125 if (fragmentCallback(queryData, fragment)) |
| 124 return true; | 126 return true; |
| 125 | 127 |
| 126 processedCharacters += fragment.length; | 128 processedCharacters += fragment.length; |
| 127 } | 129 } |
| 128 | 130 |
| 129 queryData->processedCharacters = processedCharacters; | 131 queryData->processedCharacters = processedCharacters; |
| 130 } | 132 } |
| 131 | |
| 132 return false; | 133 return false; |
| 133 } | 134 } |
| 134 | 135 |
| 135 bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData,
const SVGTextFragment& fragment, int& startPosition, int& endPosition) const | 136 static void modifyStartEndPositionsRespectingLigatures(const QueryData* queryDat
a, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
| 136 { | |
| 137 // Make <startPosition, endPosition> offsets relative to the current text bo
x. | |
| 138 startPosition -= queryData->processedCharacters; | |
| 139 endPosition -= queryData->processedCharacters; | |
| 140 | |
| 141 // Reuse the same logic used for text selection & painting, to map our | |
| 142 // query start/length into start/endPositions of the current text fragment. | |
| 143 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen
t, startPosition, endPosition)) | |
| 144 return false; | |
| 145 | |
| 146 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio
n, endPosition); | |
| 147 ASSERT(startPosition < endPosition); | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, c
onst SVGTextFragment& fragment, int& startPosition, int& endPosition) const | |
| 152 { | 137 { |
| 153 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec
t->layoutAttributes()->textMetricsValues(); | 138 const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObjec
t->layoutAttributes()->textMetricsValues(); |
| 154 | 139 |
| 155 unsigned textMetricsOffset = fragment.metricsListOffset; | 140 unsigned textMetricsOffset = fragment.metricsListOffset; |
| 156 int fragmentOffset = 0; | 141 int fragmentOffset = 0; |
| 157 int fragmentEnd = static_cast<int>(fragment.length); | 142 int fragmentEnd = static_cast<int>(fragment.length); |
| 158 | 143 |
| 159 // Find the text metrics cell that start at or contain the character startPo
sition. | 144 // Find the text metrics cell that start at or contain the character startPo
sition. |
| 160 while (fragmentOffset < fragmentEnd) { | 145 while (fragmentOffset < fragmentEnd) { |
| 161 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; | 146 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 173 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; | 158 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset]; |
| 174 fragmentOffset += metrics.length(); | 159 fragmentOffset += metrics.length(); |
| 175 if (fragmentOffset >= endPosition) | 160 if (fragmentOffset >= endPosition) |
| 176 break; | 161 break; |
| 177 textMetricsOffset++; | 162 textMetricsOffset++; |
| 178 } | 163 } |
| 179 | 164 |
| 180 endPosition = fragmentOffset; | 165 endPosition = fragmentOffset; |
| 181 } | 166 } |
| 182 | 167 |
| 168 static bool mapStartEndPositionsIntoFragmentCoordinates(const QueryData* queryDa
ta, const SVGTextFragment& fragment, int& startPosition, int& endPosition) |
| 169 { |
| 170 // Make <startPosition, endPosition> offsets relative to the current text bo
x. |
| 171 startPosition -= queryData->processedCharacters; |
| 172 endPosition -= queryData->processedCharacters; |
| 173 |
| 174 // Reuse the same logic used for text selection & painting, to map our |
| 175 // query start/length into start/endPositions of the current text fragment. |
| 176 if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragmen
t, startPosition, endPosition)) |
| 177 return false; |
| 178 |
| 179 modifyStartEndPositionsRespectingLigatures(queryData, fragment, startPositio
n, endPosition); |
| 180 ASSERT(startPosition < endPosition); |
| 181 return true; |
| 182 } |
| 183 |
| 183 // numberOfCharacters() implementation | 184 // numberOfCharacters() implementation |
| 184 bool SVGTextQuery::numberOfCharactersCallback(Data*, const SVGTextFragment&) con
st | 185 static bool numberOfCharactersCallback(QueryData*, const SVGTextFragment&) |
| 185 { | 186 { |
| 186 // no-op | 187 // no-op |
| 187 return false; | 188 return false; |
| 188 } | 189 } |
| 189 | 190 |
| 190 unsigned SVGTextQuery::numberOfCharacters() const | 191 unsigned SVGTextQuery::numberOfCharacters() const |
| 191 { | 192 { |
| 192 Data data; | 193 QueryData data; |
| 193 executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback); | 194 executeQuery(m_queryRootLayoutObject, &data, numberOfCharactersCallback); |
| 194 return data.processedCharacters; | 195 return data.processedCharacters; |
| 195 } | 196 } |
| 196 | 197 |
| 197 // textLength() implementation | 198 // textLength() implementation |
| 198 struct TextLengthData : SVGTextQuery::Data { | 199 struct TextLengthData : QueryData { |
| 199 TextLengthData() | 200 TextLengthData() |
| 200 : textLength(0) | 201 : textLength(0) |
| 201 { | 202 { |
| 202 } | 203 } |
| 203 | 204 |
| 204 float textLength; | 205 float textLength; |
| 205 }; | 206 }; |
| 206 | 207 |
| 207 bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGTextFragment& fr
agment) const | 208 static bool textLengthCallback(QueryData* queryData, const SVGTextFragment& frag
ment) |
| 208 { | 209 { |
| 209 TextLengthData* data = static_cast<TextLengthData*>(queryData); | 210 TextLengthData* data = static_cast<TextLengthData*>(queryData); |
| 210 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w
idth; | 211 data->textLength += queryData->isVerticalText ? fragment.height : fragment.w
idth; |
| 211 return false; | 212 return false; |
| 212 } | 213 } |
| 213 | 214 |
| 214 float SVGTextQuery::textLength() const | 215 float SVGTextQuery::textLength() const |
| 215 { | 216 { |
| 216 TextLengthData data; | 217 TextLengthData data; |
| 217 executeQuery(&data, &SVGTextQuery::textLengthCallback); | 218 executeQuery(m_queryRootLayoutObject, &data, textLengthCallback); |
| 218 return data.textLength; | 219 return data.textLength; |
| 219 } | 220 } |
| 220 | 221 |
| 221 // subStringLength() implementation | 222 // subStringLength() implementation |
| 222 struct SubStringLengthData : SVGTextQuery::Data { | 223 struct SubStringLengthData : QueryData { |
| 223 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) | 224 SubStringLengthData(unsigned queryStartPosition, unsigned queryLength) |
| 224 : startPosition(queryStartPosition) | 225 : startPosition(queryStartPosition) |
| 225 , length(queryLength) | 226 , length(queryLength) |
| 226 , subStringLength(0) | 227 , subStringLength(0) |
| 227 { | 228 { |
| 228 } | 229 } |
| 229 | 230 |
| 230 unsigned startPosition; | 231 unsigned startPosition; |
| 231 unsigned length; | 232 unsigned length; |
| 232 | 233 |
| 233 float subStringLength; | 234 float subStringLength; |
| 234 }; | 235 }; |
| 235 | 236 |
| 236 bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragmen
t& fragment) const | 237 static bool subStringLengthCallback(QueryData* queryData, const SVGTextFragment&
fragment) |
| 237 { | 238 { |
| 238 SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); | 239 SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); |
| 239 | 240 |
| 240 int startPosition = data->startPosition; | 241 int startPosition = data->startPosition; |
| 241 int endPosition = startPosition + data->length; | 242 int endPosition = startPosition + data->length; |
| 242 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 243 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
| 243 return false; | 244 return false; |
| 244 | 245 |
| 245 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te
xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos
ition, queryData->textBox->direction()); | 246 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->te
xtLayoutObject, fragment.characterOffset + startPosition, endPosition - startPos
ition, queryData->textBox->direction()); |
| 246 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr
ics.width(); | 247 data->subStringLength += queryData->isVerticalText ? metrics.height() : metr
ics.width(); |
| 247 return false; | 248 return false; |
| 248 } | 249 } |
| 249 | 250 |
| 250 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con
st | 251 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) con
st |
| 251 { | 252 { |
| 252 SubStringLengthData data(startPosition, length); | 253 SubStringLengthData data(startPosition, length); |
| 253 executeQuery(&data, &SVGTextQuery::subStringLengthCallback); | 254 executeQuery(m_queryRootLayoutObject, &data, subStringLengthCallback); |
| 254 return data.subStringLength; | 255 return data.subStringLength; |
| 255 } | 256 } |
| 256 | 257 |
| 257 // startPositionOfCharacter() implementation | 258 // startPositionOfCharacter() implementation |
| 258 struct StartPositionOfCharacterData : SVGTextQuery::Data { | 259 struct StartPositionOfCharacterData : QueryData { |
| 259 StartPositionOfCharacterData(unsigned queryPosition) | 260 StartPositionOfCharacterData(unsigned queryPosition) |
| 260 : position(queryPosition) | 261 : position(queryPosition) |
| 261 { | 262 { |
| 262 } | 263 } |
| 263 | 264 |
| 264 unsigned position; | 265 unsigned position; |
| 265 FloatPoint startPosition; | 266 FloatPoint startPosition; |
| 266 }; | 267 }; |
| 267 | 268 |
| 268 static FloatPoint calculateGlyphPositionWithoutTransform(const SVGTextQuery::Dat
a* queryData, const SVGTextFragment& fragment, int offsetInFragment) | 269 static FloatPoint calculateGlyphPositionWithoutTransform(const QueryData* queryD
ata, const SVGTextFragment& fragment, int offsetInFragment) |
| 269 { | 270 { |
| 270 float glyphOffsetInDirection = 0; | 271 float glyphOffsetInDirection = 0; |
| 271 if (offsetInFragment) { | 272 if (offsetInFragment) { |
| 272 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData
->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textB
ox->direction()); | 273 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData
->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textB
ox->direction()); |
| 273 if (queryData->isVerticalText) | 274 if (queryData->isVerticalText) |
| 274 glyphOffsetInDirection = metrics.height(); | 275 glyphOffsetInDirection = metrics.height(); |
| 275 else | 276 else |
| 276 glyphOffsetInDirection = metrics.width(); | 277 glyphOffsetInDirection = metrics.width(); |
| 277 } | 278 } |
| 278 | 279 |
| 279 if (!queryData->textBox->isLeftToRightDirection()) { | 280 if (!queryData->textBox->isLeftToRightDirection()) { |
| 280 float fragmentExtent = queryData->isVerticalText ? fragment.height : fra
gment.width; | 281 float fragmentExtent = queryData->isVerticalText ? fragment.height : fra
gment.width; |
| 281 glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection; | 282 glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection; |
| 282 } | 283 } |
| 283 | 284 |
| 284 FloatPoint glyphPosition(fragment.x, fragment.y); | 285 FloatPoint glyphPosition(fragment.x, fragment.y); |
| 285 if (queryData->isVerticalText) | 286 if (queryData->isVerticalText) |
| 286 glyphPosition.move(0, glyphOffsetInDirection); | 287 glyphPosition.move(0, glyphOffsetInDirection); |
| 287 else | 288 else |
| 288 glyphPosition.move(glyphOffsetInDirection, 0); | 289 glyphPosition.move(glyphOffsetInDirection, 0); |
| 289 | 290 |
| 290 return glyphPosition; | 291 return glyphPosition; |
| 291 } | 292 } |
| 292 | 293 |
| 293 static FloatPoint calculateGlyphPosition(const SVGTextQuery::Data* queryData, co
nst SVGTextFragment& fragment, int offsetInFragment) | 294 static FloatPoint calculateGlyphPosition(const QueryData* queryData, const SVGTe
xtFragment& fragment, int offsetInFragment) |
| 294 { | 295 { |
| 295 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, offsetInFragment); | 296 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, offsetInFragment); |
| 296 AffineTransform fragmentTransform; | 297 AffineTransform fragmentTransform; |
| 297 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); | 298 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); |
| 298 if (!fragmentTransform.isIdentity()) | 299 if (!fragmentTransform.isIdentity()) |
| 299 glyphPosition = fragmentTransform.mapPoint(glyphPosition); | 300 glyphPosition = fragmentTransform.mapPoint(glyphPosition); |
| 300 | 301 |
| 301 return glyphPosition; | 302 return glyphPosition; |
| 302 } | 303 } |
| 303 | 304 |
| 304 bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTe
xtFragment& fragment) const | 305 static bool startPositionOfCharacterCallback(QueryData* queryData, const SVGText
Fragment& fragment) |
| 305 { | 306 { |
| 306 StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterDat
a*>(queryData); | 307 StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterDat
a*>(queryData); |
| 307 | 308 |
| 308 int startPosition = data->position; | 309 int startPosition = data->position; |
| 309 int endPosition = startPosition + 1; | 310 int endPosition = startPosition + 1; |
| 310 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 311 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
| 311 return false; | 312 return false; |
| 312 | 313 |
| 313 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit
ion); | 314 data->startPosition = calculateGlyphPosition(queryData, fragment, startPosit
ion); |
| 314 return true; | 315 return true; |
| 315 } | 316 } |
| 316 | 317 |
| 317 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const | 318 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const |
| 318 { | 319 { |
| 319 StartPositionOfCharacterData data(position); | 320 StartPositionOfCharacterData data(position); |
| 320 executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback); | 321 executeQuery(m_queryRootLayoutObject, &data, startPositionOfCharacterCallbac
k); |
| 321 return data.startPosition; | 322 return data.startPosition; |
| 322 } | 323 } |
| 323 | 324 |
| 324 // endPositionOfCharacter() implementation | 325 // endPositionOfCharacter() implementation |
| 325 struct EndPositionOfCharacterData : SVGTextQuery::Data { | 326 struct EndPositionOfCharacterData : QueryData { |
| 326 EndPositionOfCharacterData(unsigned queryPosition) | 327 EndPositionOfCharacterData(unsigned queryPosition) |
| 327 : position(queryPosition) | 328 : position(queryPosition) |
| 328 { | 329 { |
| 329 } | 330 } |
| 330 | 331 |
| 331 unsigned position; | 332 unsigned position; |
| 332 FloatPoint endPosition; | 333 FloatPoint endPosition; |
| 333 }; | 334 }; |
| 334 | 335 |
| 335 bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGText
Fragment& fragment) const | 336 static bool endPositionOfCharacterCallback(QueryData* queryData, const SVGTextFr
agment& fragment) |
| 336 { | 337 { |
| 337 EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(
queryData); | 338 EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(
queryData); |
| 338 | 339 |
| 339 int startPosition = data->position; | 340 int startPosition = data->position; |
| 340 int endPosition = startPosition + 1; | 341 int endPosition = startPosition + 1; |
| 341 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 342 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
| 342 return false; | 343 return false; |
| 343 | 344 |
| 344 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies | 345 // TODO(fs): mapStartEndPositionsIntoFragmentCoordinates(...) above applies |
| 345 // some heuristics for ligatures, so why not just use endPosition here? | 346 // some heuristics for ligatures, so why not just use endPosition here? |
| 346 // (rather than startPosition+1) | 347 // (rather than startPosition+1) |
| 347 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio
n + 1); | 348 data->endPosition = calculateGlyphPosition(queryData, fragment, startPositio
n + 1); |
| 348 return true; | 349 return true; |
| 349 } | 350 } |
| 350 | 351 |
| 351 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const | 352 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const |
| 352 { | 353 { |
| 353 EndPositionOfCharacterData data(position); | 354 EndPositionOfCharacterData data(position); |
| 354 executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback); | 355 executeQuery(m_queryRootLayoutObject, &data, endPositionOfCharacterCallback)
; |
| 355 return data.endPosition; | 356 return data.endPosition; |
| 356 } | 357 } |
| 357 | 358 |
| 358 // rotationOfCharacter() implementation | 359 // rotationOfCharacter() implementation |
| 359 struct RotationOfCharacterData : SVGTextQuery::Data { | 360 struct RotationOfCharacterData : QueryData { |
| 360 RotationOfCharacterData(unsigned queryPosition) | 361 RotationOfCharacterData(unsigned queryPosition) |
| 361 : position(queryPosition) | 362 : position(queryPosition) |
| 362 , rotation(0) | 363 , rotation(0) |
| 363 { | 364 { |
| 364 } | 365 } |
| 365 | 366 |
| 366 unsigned position; | 367 unsigned position; |
| 367 float rotation; | 368 float rotation; |
| 368 }; | 369 }; |
| 369 | 370 |
| 370 bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGTextFra
gment& fragment) const | 371 static bool rotationOfCharacterCallback(QueryData* queryData, const SVGTextFragm
ent& fragment) |
| 371 { | 372 { |
| 372 RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryD
ata); | 373 RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryD
ata); |
| 373 | 374 |
| 374 int startPosition = data->position; | 375 int startPosition = data->position; |
| 375 int endPosition = startPosition + 1; | 376 int endPosition = startPosition + 1; |
| 376 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 377 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
| 377 return false; | 378 return false; |
| 378 | 379 |
| 379 AffineTransform fragmentTransform; | 380 AffineTransform fragmentTransform; |
| 380 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); | 381 fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::Transfor
mIgnoringTextLength); |
| 381 if (fragmentTransform.isIdentity()) { | 382 if (fragmentTransform.isIdentity()) { |
| 382 data->rotation = 0; | 383 data->rotation = 0; |
| 383 } else { | 384 } else { |
| 384 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran
sform.yScale()); | 385 fragmentTransform.scale(1 / fragmentTransform.xScale(), 1 / fragmentTran
sform.yScale()); |
| 385 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform.
b(), fragmentTransform.a()))); | 386 data->rotation = narrowPrecisionToFloat(rad2deg(atan2(fragmentTransform.
b(), fragmentTransform.a()))); |
| 386 } | 387 } |
| 387 | 388 |
| 388 return true; | 389 return true; |
| 389 } | 390 } |
| 390 | 391 |
| 391 float SVGTextQuery::rotationOfCharacter(unsigned position) const | 392 float SVGTextQuery::rotationOfCharacter(unsigned position) const |
| 392 { | 393 { |
| 393 RotationOfCharacterData data(position); | 394 RotationOfCharacterData data(position); |
| 394 executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback); | 395 executeQuery(m_queryRootLayoutObject, &data, rotationOfCharacterCallback); |
| 395 return data.rotation; | 396 return data.rotation; |
| 396 } | 397 } |
| 397 | 398 |
| 398 // extentOfCharacter() implementation | 399 // extentOfCharacter() implementation |
| 399 struct ExtentOfCharacterData : SVGTextQuery::Data { | 400 struct ExtentOfCharacterData : QueryData { |
| 400 ExtentOfCharacterData(unsigned queryPosition) | 401 ExtentOfCharacterData(unsigned queryPosition) |
| 401 : position(queryPosition) | 402 : position(queryPosition) |
| 402 { | 403 { |
| 403 } | 404 } |
| 404 | 405 |
| 405 unsigned position; | 406 unsigned position; |
| 406 FloatRect extent; | 407 FloatRect extent; |
| 407 }; | 408 }; |
| 408 | 409 |
| 409 const SVGTextMetrics& findMetricsForCharacter(const Vector<SVGTextMetrics>& text
MetricsValues, const SVGTextFragment& fragment, unsigned startInFragment) | 410 const SVGTextMetrics& findMetricsForCharacter(const Vector<SVGTextMetrics>& text
MetricsValues, const SVGTextFragment& fragment, unsigned startInFragment) |
| 410 { | 411 { |
| 411 // Find the text metrics cell that start at or contain the character at |sta
rtInFragment|. | 412 // Find the text metrics cell that start at or contain the character at |sta
rtInFragment|. |
| 412 unsigned textMetricsOffset = fragment.metricsListOffset; | 413 unsigned textMetricsOffset = fragment.metricsListOffset; |
| 413 unsigned fragmentOffset = 0; | 414 unsigned fragmentOffset = 0; |
| 414 while (fragmentOffset < fragment.length) { | 415 while (fragmentOffset < fragment.length) { |
| 415 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset++]; | 416 const SVGTextMetrics& metrics = textMetricsValues[textMetricsOffset++]; |
| 416 unsigned glyphEnd = fragmentOffset + metrics.length(); | 417 unsigned glyphEnd = fragmentOffset + metrics.length(); |
| 417 if (startInFragment < glyphEnd) | 418 if (startInFragment < glyphEnd) |
| 418 break; | 419 break; |
| 419 fragmentOffset = glyphEnd; | 420 fragmentOffset = glyphEnd; |
| 420 } | 421 } |
| 421 return textMetricsValues[textMetricsOffset - 1]; | 422 return textMetricsValues[textMetricsOffset - 1]; |
| 422 } | 423 } |
| 423 | 424 |
| 424 static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const
SVGTextFragment& fragment, int startPosition, FloatRect& extent) | 425 static inline void calculateGlyphBoundaries(const QueryData* queryData, const SV
GTextFragment& fragment, int startPosition, FloatRect& extent) |
| 425 { | 426 { |
| 426 float scalingFactor = queryData->textLayoutObject->scalingFactor(); | 427 float scalingFactor = queryData->textLayoutObject->scalingFactor(); |
| 427 ASSERT(scalingFactor); | 428 ASSERT(scalingFactor); |
| 428 | 429 |
| 429 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, startPosition); | 430 FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData,
fragment, startPosition); |
| 430 glyphPosition.move(0, -queryData->textLayoutObject->scaledFont().fontMetrics
().floatAscent() / scalingFactor); | 431 glyphPosition.move(0, -queryData->textLayoutObject->scaledFont().fontMetrics
().floatAscent() / scalingFactor); |
| 431 extent.setLocation(glyphPosition); | 432 extent.setLocation(glyphPosition); |
| 432 | 433 |
| 433 // Use the SVGTextMetrics computed by SVGTextMetricsBuilder (which spends | 434 // Use the SVGTextMetrics computed by SVGTextMetricsBuilder (which spends |
| 434 // time attempting to compute more correct glyph bounds already, handling | 435 // time attempting to compute more correct glyph bounds already, handling |
| (...skipping 25 matching lines...) Expand all Loading... |
| 460 float scalingFactor = textLayoutObject.scalingFactor(); | 461 float scalingFactor = textLayoutObject.scalingFactor(); |
| 461 ASSERT(scalingFactor); | 462 ASSERT(scalingFactor); |
| 462 float baseline = textLayoutObject.scaledFont().fontMetrics().floatAscent() /
scalingFactor; | 463 float baseline = textLayoutObject.scaledFont().fontMetrics().floatAscent() /
scalingFactor; |
| 463 | 464 |
| 464 AffineTransform fragmentTransform; | 465 AffineTransform fragmentTransform; |
| 465 FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fr
agment.height); | 466 FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fr
agment.height); |
| 466 fragment.buildFragmentTransform(fragmentTransform); | 467 fragment.buildFragmentTransform(fragmentTransform); |
| 467 return fragmentTransform.mapRect(fragmentRect); | 468 return fragmentTransform.mapRect(fragmentRect); |
| 468 } | 469 } |
| 469 | 470 |
| 470 bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGTextFragm
ent& fragment) const | 471 static bool extentOfCharacterCallback(QueryData* queryData, const SVGTextFragmen
t& fragment) |
| 471 { | 472 { |
| 472 ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData)
; | 473 ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData)
; |
| 473 | 474 |
| 474 int startPosition = data->position; | 475 int startPosition = data->position; |
| 475 int endPosition = startPosition + 1; | 476 int endPosition = startPosition + 1; |
| 476 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) | 477 if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startP
osition, endPosition)) |
| 477 return false; | 478 return false; |
| 478 | 479 |
| 479 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); | 480 calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent); |
| 480 return true; | 481 return true; |
| 481 } | 482 } |
| 482 | 483 |
| 483 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const | 484 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const |
| 484 { | 485 { |
| 485 ExtentOfCharacterData data(position); | 486 ExtentOfCharacterData data(position); |
| 486 executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback); | 487 executeQuery(m_queryRootLayoutObject, &data, extentOfCharacterCallback); |
| 487 return data.extent; | 488 return data.extent; |
| 488 } | 489 } |
| 489 | 490 |
| 490 // characterNumberAtPosition() implementation | 491 // characterNumberAtPosition() implementation |
| 491 struct CharacterNumberAtPositionData : SVGTextQuery::Data { | 492 struct CharacterNumberAtPositionData : QueryData { |
| 492 CharacterNumberAtPositionData(const FloatPoint& queryPosition) | 493 CharacterNumberAtPositionData(const FloatPoint& queryPosition) |
| 493 : position(queryPosition) | 494 : position(queryPosition) |
| 494 { | 495 { |
| 495 } | 496 } |
| 496 | 497 |
| 497 FloatPoint position; | 498 FloatPoint position; |
| 498 }; | 499 }; |
| 499 | 500 |
| 500 bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGT
extFragment& fragment) const | 501 static bool characterNumberAtPositionCallback(QueryData* queryData, const SVGTex
tFragment& fragment) |
| 501 { | 502 { |
| 502 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD
ata*>(queryData); | 503 CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionD
ata*>(queryData); |
| 503 | 504 |
| 504 // Test the query point against the bounds of the entire fragment first. | 505 // Test the query point against the bounds of the entire fragment first. |
| 505 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo
utObject, fragment); | 506 FloatRect fragmentExtents = calculateFragmentBoundaries(*queryData->textLayo
utObject, fragment); |
| 506 if (!fragmentExtents.contains(data->position)) | 507 if (!fragmentExtents.contains(data->position)) |
| 507 return false; | 508 return false; |
| 508 | 509 |
| 509 // Iterate through the glyphs in this fragment, and check if their extents | 510 // Iterate through the glyphs in this fragment, and check if their extents |
| 510 // contain the query point. | 511 // contain the query point. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 523 } | 524 } |
| 524 fragmentOffset += textMetrics[textMetricsOffset].length(); | 525 fragmentOffset += textMetrics[textMetricsOffset].length(); |
| 525 textMetricsOffset++; | 526 textMetricsOffset++; |
| 526 } | 527 } |
| 527 return false; | 528 return false; |
| 528 } | 529 } |
| 529 | 530 |
| 530 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const | 531 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const |
| 531 { | 532 { |
| 532 CharacterNumberAtPositionData data(position); | 533 CharacterNumberAtPositionData data(position); |
| 533 if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback)) | 534 if (!executeQuery(m_queryRootLayoutObject, &data, characterNumberAtPositionC
allback)) |
| 534 return -1; | 535 return -1; |
| 535 | 536 |
| 536 return data.processedCharacters; | 537 return data.processedCharacters; |
| 537 } | 538 } |
| 538 | 539 |
| 539 } | 540 } |
| OLD | NEW |