Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010. 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/SVGTextChunkBuilder.h" | 21 #include "core/layout/svg/SVGTextChunkBuilder.h" |
| 22 | 22 |
| 23 #include "core/layout/svg/LayoutSVGInlineText.h" | 23 #include "core/layout/svg/LayoutSVGInlineText.h" |
| 24 #include "core/layout/svg/SVGTextChunk.h" | |
| 25 #include "core/layout/svg/line/SVGInlineTextBox.h" | 24 #include "core/layout/svg/line/SVGInlineTextBox.h" |
| 26 #include "core/svg/SVGLengthContext.h" | 25 #include "core/svg/SVGLengthContext.h" |
| 27 #include "core/svg/SVGTextContentElement.h" | 26 #include "core/svg/SVGTextContentElement.h" |
| 28 | 27 |
| 29 namespace blink { | 28 namespace blink { |
| 30 | 29 |
| 31 namespace { | 30 namespace { |
| 32 | 31 |
| 33 SVGTextChunk createTextChunk(const SVGInlineTextBox& textBox) | 32 float calculateTextAnchorShift(const ComputedStyle& style, float length) |
| 34 { | 33 { |
| 35 LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(textBox.layout Object()); | 34 ETextAnchor textAnchor = style.svgStyle().textAnchor(); |
| 36 const ComputedStyle& style = textLayoutObject.styleRef(); | 35 if (textAnchor == TA_MIDDLE) |
| 37 const SVGComputedStyle& svgStyle = style.svgStyle(); | 36 return -length / 2; |
| 37 bool isLTR = style.isLeftToRightDirection(); | |
| 38 if (textAnchor == TA_END) | |
| 39 return isLTR ? -length : 0; | |
| 40 return isLTR ? 0 : -length; | |
| 41 } | |
| 38 | 42 |
| 39 // Build chunk style flags. | 43 bool needsTextAnchorAdjustment(const ComputedStyle& style) |
| 40 unsigned chunkStyle = SVGTextChunk::DefaultStyle; | 44 { |
| 41 | 45 ETextAnchor textAnchor = style.svgStyle().textAnchor(); |
|
pdr.
2015/06/04 20:22:02
Nit: These may be a little cleaner as a switch sta
fs
2015/06/05 08:42:32
I had this in PS1, but then some of the bots bombe
| |
| 42 // Handle 'direction' property. | 46 if (textAnchor == TA_MIDDLE) |
| 43 if (!style.isLeftToRightDirection()) | 47 return true; |
| 44 chunkStyle |= SVGTextChunk::RightToLeftText; | 48 bool isLTR = style.isLeftToRightDirection(); |
| 45 | 49 return (isLTR && textAnchor == TA_END) |
| 46 // Handle 'writing-mode' property. | 50 || (!isLTR && textAnchor == TA_START); |
| 47 if (svgStyle.isVerticalWritingMode()) | |
| 48 chunkStyle |= SVGTextChunk::VerticalText; | |
| 49 | |
| 50 // Handle 'text-anchor' property. | |
| 51 switch (svgStyle.textAnchor()) { | |
| 52 case TA_START: | |
| 53 break; | |
| 54 case TA_MIDDLE: | |
| 55 chunkStyle |= SVGTextChunk::MiddleAnchor; | |
| 56 break; | |
| 57 case TA_END: | |
| 58 chunkStyle |= SVGTextChunk::EndAnchor; | |
| 59 break; | |
| 60 } | |
| 61 | |
| 62 // Handle 'lengthAdjust' property. | |
| 63 float desiredTextLength = 0; | |
| 64 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme ntFromLayoutObject(textLayoutObject.parent())) { | |
| 65 SVGLengthContext lengthContext(textContentElement); | |
| 66 if (textContentElement->textLengthIsSpecifiedByUser()) | |
| 67 desiredTextLength = textContentElement->textLength()->currentValue() ->value(lengthContext); | |
| 68 else | |
| 69 desiredTextLength = 0; | |
| 70 | |
| 71 switch (textContentElement->lengthAdjust()->currentValue()->enumValue()) { | |
| 72 case SVGLengthAdjustUnknown: | |
| 73 break; | |
| 74 case SVGLengthAdjustSpacing: | |
| 75 chunkStyle |= SVGTextChunk::LengthAdjustSpacing; | |
| 76 break; | |
| 77 case SVGLengthAdjustSpacingAndGlyphs: | |
| 78 chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; | |
| 79 break; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 return SVGTextChunk(chunkStyle, desiredTextLength); | |
| 84 } | 51 } |
| 85 | 52 |
| 86 class ChunkLengthAccumulator { | 53 class ChunkLengthAccumulator { |
| 87 public: | 54 public: |
| 88 ChunkLengthAccumulator(bool isVertical) | 55 ChunkLengthAccumulator(bool isVertical) |
| 89 : m_numCharacters(0) | 56 : m_numCharacters(0) |
| 90 , m_length(0) | 57 , m_length(0) |
| 91 , m_isVertical(isVertical) | 58 , m_isVertical(isVertical) |
| 92 { | 59 { |
| 93 } | 60 } |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 176 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() | 143 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
| 177 : SVGTextChunkBuilder() | 144 : SVGTextChunkBuilder() |
| 178 , m_totalLength(0) | 145 , m_totalLength(0) |
| 179 , m_totalCharacters(0) | 146 , m_totalCharacters(0) |
| 180 , m_totalTextAnchorShift(0) | 147 , m_totalTextAnchorShift(0) |
| 181 { | 148 { |
| 182 } | 149 } |
| 183 | 150 |
| 184 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box ListConstIterator boxEnd) | 151 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box ListConstIterator boxEnd) |
| 185 { | 152 { |
| 186 SVGTextChunk chunk = createTextChunk(**boxStart); | 153 const ComputedStyle& style = (*boxStart)->layoutObject().styleRef(); |
| 187 | 154 |
| 188 ChunkLengthAccumulator lengthAccumulator(chunk.isVerticalText()); | 155 ChunkLengthAccumulator lengthAccumulator(style.svgStyle().isVerticalWritingM ode()); |
| 189 lengthAccumulator.processRange(boxStart, boxEnd); | 156 lengthAccumulator.processRange(boxStart, boxEnd); |
| 190 | 157 |
| 191 // Handle text-anchor as additional start offset for text paths. | 158 // Handle text-anchor as additional start offset for text paths. |
| 192 m_totalTextAnchorShift += chunk.calculateTextAnchorShift(lengthAccumulator.l ength()); | 159 m_totalTextAnchorShift += calculateTextAnchorShift(style, lengthAccumulator. length()); |
| 193 | 160 |
| 194 m_totalLength += lengthAccumulator.length(); | 161 m_totalLength += lengthAccumulator.length(); |
| 195 m_totalCharacters += lengthAccumulator.numCharacters(); | 162 m_totalCharacters += lengthAccumulator.numCharacters(); |
| 196 } | 163 } |
| 197 | 164 |
| 198 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) | 165 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) |
| 199 { | 166 { |
| 200 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); | 167 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); |
| 201 | 168 |
| 202 if (isVerticalText) | 169 if (isVerticalText) |
| 203 spacingAndGlyphsTransform.scaleNonUniform(1, scale); | 170 spacingAndGlyphsTransform.scaleNonUniform(1, scale); |
| 204 else | 171 else |
| 205 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); | 172 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); |
| 206 | 173 |
| 207 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); | 174 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); |
| 208 } | 175 } |
| 209 | 176 |
| 210 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList ConstIterator boxEnd) | 177 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList ConstIterator boxEnd) |
| 211 { | 178 { |
| 212 SVGTextChunk chunk = createTextChunk(**boxStart); | 179 ASSERT(*boxStart); |
| 213 | 180 |
| 214 bool processTextLength = chunk.hasDesiredTextLength(); | 181 const LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText((*boxSta rt)->layoutObject()); |
| 215 bool processTextAnchor = chunk.hasTextAnchor(); | 182 const ComputedStyle& style = textLayoutObject.styleRef(); |
| 183 | |
| 184 // Handle 'lengthAdjust' property. | |
| 185 float desiredTextLength = 0; | |
| 186 SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; | |
| 187 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme ntFromLayoutObject(textLayoutObject.parent())) { | |
| 188 lengthAdjust = textContentElement->lengthAdjust()->currentValue()->enumV alue(); | |
| 189 | |
| 190 SVGLengthContext lengthContext(textContentElement); | |
| 191 if (textContentElement->textLengthIsSpecifiedByUser()) | |
| 192 desiredTextLength = textContentElement->textLength()->currentValue() ->value(lengthContext); | |
| 193 else | |
| 194 desiredTextLength = 0; | |
| 195 } | |
| 196 | |
| 197 bool processTextLength = desiredTextLength > 0; | |
| 198 bool processTextAnchor = needsTextAnchorAdjustment(style); | |
| 216 if (!processTextAnchor && !processTextLength) | 199 if (!processTextAnchor && !processTextLength) |
| 217 return; | 200 return; |
| 218 | 201 |
| 219 bool isVerticalText = chunk.isVerticalText(); | 202 bool isVerticalText = style.svgStyle().isVerticalWritingMode(); |
| 220 | 203 |
| 221 // Calculate absolute length of whole text chunk (starting from text box 'st art', spanning 'length' text boxes). | 204 // Calculate absolute length of whole text chunk (starting from text box 'st art', spanning 'length' text boxes). |
| 222 ChunkLengthAccumulator lengthAccumulator(isVerticalText); | 205 ChunkLengthAccumulator lengthAccumulator(isVerticalText); |
| 223 lengthAccumulator.processRange(boxStart, boxEnd); | 206 lengthAccumulator.processRange(boxStart, boxEnd); |
| 224 | 207 |
| 225 if (processTextLength) { | 208 if (processTextLength) { |
| 226 float chunkLength = lengthAccumulator.length(); | 209 float chunkLength = lengthAccumulator.length(); |
| 227 if (chunk.hasLengthAdjustSpacing()) { | 210 if (lengthAdjust == SVGLengthAdjustSpacing) { |
| 228 float textLengthShift = (chunk.desiredTextLength() - chunkLength) / lengthAccumulator.numCharacters(); | 211 float textLengthShift = (desiredTextLength - chunkLength) / lengthAc cumulator.numCharacters(); |
| 229 unsigned atCharacter = 0; | 212 unsigned atCharacter = 0; |
| 230 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 213 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 231 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments() ; | 214 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments() ; |
| 232 if (fragments.isEmpty()) | 215 if (fragments.isEmpty()) |
| 233 continue; | 216 continue; |
| 234 processTextLengthSpacingCorrection(isVerticalText, textLengthShi ft, fragments, atCharacter); | 217 processTextLengthSpacingCorrection(isVerticalText, textLengthShi ft, fragments, atCharacter); |
| 235 } | 218 } |
| 236 | 219 |
| 237 // Fragments have been adjusted, we have to recalculate the chunk | 220 // Fragments have been adjusted, we have to recalculate the chunk |
| 238 // length, to be able to apply the text-anchor shift. | 221 // length, to be able to apply the text-anchor shift. |
| 239 if (processTextAnchor) { | 222 if (processTextAnchor) { |
| 240 lengthAccumulator.reset(); | 223 lengthAccumulator.reset(); |
| 241 lengthAccumulator.processRange(boxStart, boxEnd); | 224 lengthAccumulator.processRange(boxStart, boxEnd); |
| 242 } | 225 } |
| 243 } else { | 226 } else { |
| 244 ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); | 227 ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs); |
| 245 float textLengthScale = chunk.desiredTextLength() / chunkLength; | 228 float textLengthScale = desiredTextLength / chunkLength; |
| 246 AffineTransform spacingAndGlyphsTransform; | 229 AffineTransform spacingAndGlyphsTransform; |
| 247 | 230 |
| 248 bool foundFirstFragment = false; | 231 bool foundFirstFragment = false; |
| 249 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 232 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 250 SVGInlineTextBox* textBox = *boxIter; | 233 SVGInlineTextBox* textBox = *boxIter; |
| 251 Vector<SVGTextFragment>& fragments = textBox->textFragments(); | 234 Vector<SVGTextFragment>& fragments = textBox->textFragments(); |
| 252 if (fragments.isEmpty()) | 235 if (fragments.isEmpty()) |
| 253 continue; | 236 continue; |
| 254 | 237 |
| 255 if (!foundFirstFragment) { | 238 if (!foundFirstFragment) { |
| 256 foundFirstFragment = true; | 239 foundFirstFragment = true; |
| 257 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca le, fragments.first(), spacingAndGlyphsTransform); | 240 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca le, fragments.first(), spacingAndGlyphsTransform); |
| 258 } | 241 } |
| 259 | 242 |
| 260 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform) ; | 243 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform) ; |
| 261 } | 244 } |
| 262 } | 245 } |
| 263 } | 246 } |
| 264 | 247 |
| 265 if (!processTextAnchor) | 248 if (!processTextAnchor) |
| 266 return; | 249 return; |
| 267 | 250 |
| 268 float textAnchorShift = chunk.calculateTextAnchorShift(lengthAccumulator.len gth()); | 251 float textAnchorShift = calculateTextAnchorShift(style, lengthAccumulator.le ngth()); |
| 269 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 252 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 270 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); | 253 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
| 271 if (fragments.isEmpty()) | 254 if (fragments.isEmpty()) |
| 272 continue; | 255 continue; |
| 273 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); | 256 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); |
| 274 } | 257 } |
| 275 } | 258 } |
| 276 | 259 |
| 277 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText , float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact er) | 260 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText , float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact er) |
| 278 { | 261 { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 continue; | 296 continue; |
| 314 | 297 |
| 315 for (SVGTextFragment& fragment : textBox->textFragments()) { | 298 for (SVGTextFragment& fragment : textBox->textFragments()) { |
| 316 ASSERT(fragment.lengthAdjustTransform.isIdentity()); | 299 ASSERT(fragment.lengthAdjustTransform.isIdentity()); |
| 317 fragment.lengthAdjustTransform = textBoxTransformation; | 300 fragment.lengthAdjustTransform = textBoxTransformation; |
| 318 } | 301 } |
| 319 } | 302 } |
| 320 } | 303 } |
| 321 | 304 |
| 322 } | 305 } |
| OLD | NEW |