WebCore/rendering/SVGTextQuery.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2     Copyright (C) Research In Motion Limited 2010. All rights reserved.
       
     3 
       
     4     This library is free software; you can redistribute it and/or
       
     5     modify it under the terms of the GNU Library General Public
       
     6     License as published by the Free Software Foundation; either
       
     7     version 2 of the License, or (at your option) any later version.
       
     8 
       
     9     This library is distributed in the hope that it will be useful,
       
    10     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12     Library General Public License for more details.
       
    13 
       
    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
       
    16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    17     Boston, MA 02110-1301, USA.
       
    18 */
       
    19 
       
    20 #include "config.h"
       
    21 #include "SVGTextQuery.h"
       
    22 
       
    23 #if ENABLE(SVG)
       
    24 #include "FloatConversion.h"
       
    25 #include "InlineFlowBox.h"
       
    26 #include "RenderBlock.h"
       
    27 #include "RenderInline.h"
       
    28 #include "SVGInlineTextBox.h"
       
    29 #include "VisiblePosition.h"
       
    30 
       
    31 namespace WebCore {
       
    32 
       
    33 // Base structure for callback user data
       
    34 struct SVGTextQuery::Data {
       
    35     Data()
       
    36         : processedChunkCharacters(0)
       
    37     {
       
    38     }
       
    39 
       
    40     unsigned processedChunkCharacters;
       
    41 };
       
    42 
       
    43 static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer)
       
    44 {
       
    45     if (!renderer)
       
    46         return 0;
       
    47 
       
    48     if (renderer->isRenderBlock()) {
       
    49         // If we're given a block element, it has to be a RenderSVGText.
       
    50         ASSERT(renderer->isSVGText());
       
    51         RenderBlock* renderBlock = toRenderBlock(renderer);
       
    52 
       
    53         // RenderSVGText only ever contains a single line box.
       
    54         InlineFlowBox* flowBox = renderBlock->firstLineBox();
       
    55         ASSERT(flowBox == renderBlock->lastLineBox());
       
    56         return flowBox;
       
    57     }
       
    58 
       
    59     if (renderer->isRenderInline()) {
       
    60         // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath)
       
    61         RenderInline* renderInline = toRenderInline(renderer);
       
    62 
       
    63         // RenderSVGInline only ever contains a single line box.
       
    64         InlineFlowBox* flowBox = renderInline->firstLineBox();
       
    65         ASSERT(flowBox == renderInline->lastLineBox());
       
    66         return flowBox;
       
    67     }
       
    68 
       
    69     ASSERT_NOT_REACHED();
       
    70     return 0;
       
    71 }
       
    72 
       
    73 static inline float mapLengthThroughChunkTransformation(const SVGInlineTextBox* textBox, bool isVerticalText, float length)
       
    74 {
       
    75     const AffineTransform& transform = textBox->chunkTransformation();
       
    76     if (transform.isIdentity())
       
    77         return length;
       
    78         
       
    79     return narrowPrecisionToFloat(static_cast<double>(length) * (isVerticalText ? transform.d() : transform.a()));
       
    80 }
       
    81 
       
    82 SVGTextQuery::SVGTextQuery(RenderObject* renderer)
       
    83 {
       
    84     collectTextBoxesInFlowBox(flowBoxForRenderer(renderer));
       
    85 }
       
    86 
       
    87 void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox)
       
    88 {
       
    89     if (!flowBox)
       
    90         return;
       
    91 
       
    92     for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) {
       
    93         if (child->isInlineFlowBox()) {
       
    94             // Skip generated content.
       
    95             if (!child->renderer()->node())
       
    96                 continue;
       
    97 
       
    98             collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child));
       
    99             continue;
       
   100         }
       
   101 
       
   102         ASSERT(child->isSVGInlineTextBox());
       
   103         m_textBoxes.append(static_cast<SVGInlineTextBox*>(child));
       
   104     }
       
   105 }
       
   106 
       
   107 bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextChunkPartCallback chunkPartCallback) const
       
   108 {
       
   109     ASSERT(!m_textBoxes.isEmpty());
       
   110     bool finished = false;
       
   111 
       
   112     // Loop over all text boxes
       
   113     const Vector<SVGInlineTextBox*>::const_iterator end = m_textBoxes.end();
       
   114     for (Vector<SVGInlineTextBox*>::const_iterator it = m_textBoxes.begin(); it != end; ++it) {
       
   115         const SVGInlineTextBox* textBox = *it;
       
   116         const Vector<SVGTextChunkPart>& parts = textBox->svgTextChunkParts();
       
   117 
       
   118         int processedCharacters = 0;
       
   119 
       
   120         // Loop over all text chunk parts in this text box, firing a callback for each chunk part.
       
   121         const Vector<SVGTextChunkPart>::const_iterator partEnd = parts.end();
       
   122         for (Vector<SVGTextChunkPart>::const_iterator partIt = parts.begin(); partIt != partEnd; ++partIt) {
       
   123             if ((this->*chunkPartCallback)(queryData, textBox, *partIt)) {
       
   124                 finished = true;
       
   125                 break;
       
   126             }
       
   127 
       
   128             processedCharacters += partIt->length;
       
   129         }
       
   130 
       
   131         if (finished)
       
   132             break;
       
   133 
       
   134         queryData->processedChunkCharacters += processedCharacters;
       
   135     }
       
   136 
       
   137     return finished;
       
   138 }
       
   139 
       
   140 bool SVGTextQuery::mapStartAndLengthIntoChunkPartCoordinates(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part, int& startPosition, int& endPosition) const
       
   141 {
       
   142     // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text chunk part.
       
   143     startPosition -= queryData->processedChunkCharacters;
       
   144     endPosition -= queryData->processedChunkCharacters;
       
   145     textBox->mapStartEndPositionsIntoChunkPartCoordinates(startPosition, endPosition, part);
       
   146 
       
   147     // If startPosition < endPosition, then the position we're supposed to measure lies in this chunk part.
       
   148     return startPosition < endPosition;
       
   149 }
       
   150 
       
   151 float SVGTextQuery::measureCharacterRange(const SVGInlineTextBox* textBox, RenderStyle* style, bool isVerticalText, int startPosition, int length) const
       
   152 {
       
   153     // FIXME: Vertical writing mode needs to be handled more accurate.
       
   154     if (isVerticalText)
       
   155         return length * style->font().height();
       
   156 
       
   157     const UChar* startCharacter = textBox->textRenderer()->characters() + textBox->start() + startPosition;
       
   158     return style->font().floatWidth(svgTextRunForInlineTextBox(startCharacter, length, style, textBox));
       
   159 }
       
   160 
       
   161 // numberOfCharacters() implementation
       
   162 struct NumberOfCharactersData : SVGTextQuery::Data {
       
   163     NumberOfCharactersData()
       
   164         : characters(0)
       
   165     {
       
   166     }
       
   167 
       
   168     unsigned characters;
       
   169 };
       
   170 
       
   171 bool SVGTextQuery::numberOfCharactersCallback(Data* queryData, const SVGInlineTextBox*, const SVGTextChunkPart& part) const
       
   172 {
       
   173     NumberOfCharactersData* data = static_cast<NumberOfCharactersData*>(queryData);
       
   174     data->characters += part.length;
       
   175     return false;
       
   176 }
       
   177 
       
   178 unsigned SVGTextQuery::numberOfCharacters() const
       
   179 {
       
   180     if (m_textBoxes.isEmpty())
       
   181         return 0;
       
   182 
       
   183     NumberOfCharactersData data;
       
   184     executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback);
       
   185     return data.characters;
       
   186 }
       
   187 
       
   188 // textLength() implementation
       
   189 struct TextLengthData : SVGTextQuery::Data {
       
   190     TextLengthData()
       
   191         : textLength(0.0f)
       
   192     {
       
   193     }
       
   194 
       
   195     float textLength;
       
   196 };
       
   197 
       
   198 bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   199 {
       
   200     TextLengthData* data = static_cast<TextLengthData*>(queryData);
       
   201 
       
   202     RenderStyle* style = textBox->textRenderer()->style();
       
   203     ASSERT(style);
       
   204 
       
   205     bool isVerticalText = isVerticalWritingMode(style->svgStyle());
       
   206     float partLength = isVerticalText ? part.height : part.width;
       
   207 
       
   208     data->textLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength);
       
   209     return false;
       
   210 }
       
   211 
       
   212 float SVGTextQuery::textLength() const
       
   213 {
       
   214     if (m_textBoxes.isEmpty())
       
   215         return 0.0f;
       
   216 
       
   217     TextLengthData data;
       
   218     executeQuery(&data, &SVGTextQuery::textLengthCallback);
       
   219     return data.textLength;
       
   220 }
       
   221 
       
   222 // subStringLength() implementation
       
   223 struct SubStringLengthData : SVGTextQuery::Data {
       
   224     SubStringLengthData(unsigned queryStartPosition, unsigned queryLength)
       
   225         : startPosition(queryStartPosition)
       
   226         , length(queryLength)
       
   227         , subStringLength(0.0f)
       
   228     {
       
   229     }
       
   230 
       
   231     unsigned startPosition;
       
   232     unsigned length;
       
   233 
       
   234     float subStringLength;
       
   235 };
       
   236 
       
   237 bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   238 {
       
   239     SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData);
       
   240 
       
   241     int startPosition = data->startPosition;
       
   242     int endPosition = startPosition + data->length;
       
   243     if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition))
       
   244         return false;
       
   245 
       
   246     RenderStyle* style = textBox->textRenderer()->style();
       
   247     ASSERT(style);
       
   248 
       
   249     bool isVerticalText = isVerticalWritingMode(style->svgStyle());
       
   250     float partLength = measureCharacterRange(textBox, style, isVerticalWritingMode(style->svgStyle()), part.offset + startPosition, endPosition - startPosition);
       
   251 
       
   252     data->subStringLength += mapLengthThroughChunkTransformation(textBox, isVerticalText, partLength);
       
   253     return false;
       
   254 }
       
   255 
       
   256 float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const
       
   257 {
       
   258     if (m_textBoxes.isEmpty())
       
   259         return 0.0f;
       
   260 
       
   261     SubStringLengthData data(startPosition, length);
       
   262     executeQuery(&data, &SVGTextQuery::subStringLengthCallback);
       
   263     return data.subStringLength;
       
   264 }
       
   265 
       
   266 // startPositionOfCharacter() implementation
       
   267 struct StartPositionOfCharacterData : SVGTextQuery::Data {
       
   268     StartPositionOfCharacterData(unsigned queryPosition)
       
   269         : position(queryPosition)
       
   270     {
       
   271     }
       
   272 
       
   273     unsigned position;
       
   274     FloatPoint startPosition;
       
   275 };
       
   276 
       
   277 bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   278 {
       
   279     StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData);
       
   280 
       
   281     int startPosition = data->position;
       
   282     int endPosition = startPosition + 1;
       
   283     if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition))
       
   284         return false;
       
   285 
       
   286     const SVGChar& character = *(part.firstCharacter + startPosition);
       
   287     data->startPosition = textBox->chunkTransformation().mapPoint(FloatPoint(character.x, character.y));
       
   288     return true;
       
   289 }
       
   290 
       
   291 FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const
       
   292 {
       
   293     if (m_textBoxes.isEmpty())
       
   294         return FloatPoint();
       
   295 
       
   296     StartPositionOfCharacterData data(position);
       
   297     executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback);
       
   298     return data.startPosition;
       
   299 }
       
   300 
       
   301 // endPositionOfCharacter() implementation
       
   302 struct EndPositionOfCharacterData : SVGTextQuery::Data {
       
   303     EndPositionOfCharacterData(unsigned queryPosition)
       
   304         : position(queryPosition)
       
   305     {
       
   306     }
       
   307 
       
   308     unsigned position;
       
   309     FloatPoint endPosition;
       
   310 };
       
   311 
       
   312 bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   313 {
       
   314     EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData);
       
   315 
       
   316     int startPosition = data->position;
       
   317     int endPosition = startPosition + 1;
       
   318     if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition))
       
   319         return false;
       
   320 
       
   321     const SVGChar& character = *(part.firstCharacter + startPosition);
       
   322     data->endPosition = FloatPoint(character.x, character.y);
       
   323 
       
   324     RenderStyle* style = textBox->textRenderer()->style();
       
   325     ASSERT(style);
       
   326 
       
   327     bool isVerticalText = isVerticalWritingMode(style->svgStyle());
       
   328     float glyphAdvance = measureCharacterRange(textBox, style, isVerticalText, part.offset + startPosition, 1);
       
   329 
       
   330     if (isVerticalText)
       
   331         data->endPosition.move(0.0f, glyphAdvance);
       
   332     else
       
   333         data->endPosition.move(glyphAdvance, 0.0f);
       
   334 
       
   335     data->endPosition = textBox->chunkTransformation().mapPoint(data->endPosition);
       
   336     return true;
       
   337 }
       
   338 
       
   339 FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const
       
   340 {
       
   341     if (m_textBoxes.isEmpty())
       
   342         return FloatPoint();
       
   343 
       
   344     EndPositionOfCharacterData data(position);
       
   345     executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback);
       
   346     return data.endPosition;
       
   347 }
       
   348 
       
   349 // rotationOfCharacter() implementation
       
   350 struct RotationOfCharacterData : SVGTextQuery::Data {
       
   351     RotationOfCharacterData(unsigned queryPosition)
       
   352         : position(queryPosition)
       
   353         , rotation(0.0f)
       
   354     {
       
   355     }
       
   356 
       
   357     unsigned position;
       
   358     float rotation;
       
   359 };
       
   360 
       
   361 bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   362 {
       
   363     RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData);
       
   364 
       
   365     int startPosition = data->position;
       
   366     int endPosition = startPosition + 1;
       
   367     if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition))
       
   368         return false;
       
   369 
       
   370     const SVGChar& character = *(part.firstCharacter + startPosition);
       
   371     data->rotation = character.angle;
       
   372     return true;
       
   373 }
       
   374 
       
   375 float SVGTextQuery::rotationOfCharacter(unsigned position) const
       
   376 {
       
   377     if (m_textBoxes.isEmpty())
       
   378         return 0.0f;
       
   379 
       
   380     RotationOfCharacterData data(position);
       
   381     executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback);
       
   382     return data.rotation;
       
   383 }
       
   384 
       
   385 // extentOfCharacter() implementation
       
   386 struct ExtentOfCharacterData : SVGTextQuery::Data {
       
   387     ExtentOfCharacterData(unsigned queryPosition)
       
   388         : position(queryPosition)
       
   389     {
       
   390     }
       
   391 
       
   392     unsigned position;
       
   393     FloatRect extent;
       
   394 };
       
   395 
       
   396 bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   397 {
       
   398     ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData);
       
   399 
       
   400     int startPosition = data->position;
       
   401     int endPosition = startPosition + 1;
       
   402     if (!mapStartAndLengthIntoChunkPartCoordinates(queryData, textBox, part, startPosition, endPosition))
       
   403         return false;
       
   404  
       
   405     RenderStyle* style = textBox->textRenderer()->style();
       
   406     ASSERT(style);
       
   407 
       
   408     const SVGChar& character = *(part.firstCharacter + startPosition);
       
   409     data->extent = textBox->calculateGlyphBoundaries(style, part.offset + startPosition, character);
       
   410     return true;
       
   411 }
       
   412 
       
   413 FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const
       
   414 {
       
   415     if (m_textBoxes.isEmpty())
       
   416         return FloatRect();
       
   417 
       
   418     ExtentOfCharacterData data(position);
       
   419     executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback);
       
   420     return data.extent;
       
   421 }
       
   422 
       
   423 // characterNumberAtPosition() implementation
       
   424 struct CharacterNumberAtPositionData : SVGTextQuery::Data {
       
   425     CharacterNumberAtPositionData(const FloatPoint& queryPosition)
       
   426         : characterNumber(0)
       
   427         , position(queryPosition)
       
   428     {
       
   429     }
       
   430 
       
   431     unsigned characterNumber;
       
   432     FloatPoint position;
       
   433 };
       
   434 
       
   435 bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGInlineTextBox* textBox, const SVGTextChunkPart& part) const
       
   436 {
       
   437     CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData);
       
   438 
       
   439     RenderStyle* style = textBox->textRenderer()->style();
       
   440     ASSERT(style);
       
   441 
       
   442     for (int i = 0; i < part.length; ++i) {
       
   443         FloatRect extent(textBox->calculateGlyphBoundaries(style, part.offset + i, *(part.firstCharacter + i)));
       
   444         if (extent.contains(data->position))
       
   445             return true;
       
   446 
       
   447         ++data->characterNumber;
       
   448     }
       
   449 
       
   450     return false;
       
   451 }
       
   452 
       
   453 int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const
       
   454 {
       
   455     if (m_textBoxes.isEmpty())
       
   456         return -1;
       
   457 
       
   458     CharacterNumberAtPositionData data(position);
       
   459     if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback))
       
   460         return -1;
       
   461 
       
   462     return data.characterNumber;
       
   463 }
       
   464 
       
   465 }
       
   466 
       
   467 #endif