| 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 bool isLTR = style.isLeftToRightDirection(); |
| 36 const ComputedStyle& style = textLayoutObject.styleRef(); | 35 switch (style.svgStyle().textAnchor()) { |
| 37 const SVGComputedStyle& svgStyle = style.svgStyle(); | 36 default: |
| 37 ASSERT_NOT_REACHED(); |
| 38 case TA_START: |
| 39 return isLTR ? 0 : -length; |
| 40 case TA_MIDDLE: |
| 41 return -length / 2; |
| 42 case TA_END: |
| 43 return isLTR ? -length : 0; |
| 44 } |
| 45 } |
| 38 | 46 |
| 39 // Build chunk style flags. | 47 bool needsTextAnchorAdjustment(const ComputedStyle& style) |
| 40 unsigned chunkStyle = SVGTextChunk::DefaultStyle; | 48 { |
| 41 | 49 bool isLTR = style.isLeftToRightDirection(); |
| 42 // Handle 'direction' property. | 50 switch (style.svgStyle().textAnchor()) { |
| 43 if (!style.isLeftToRightDirection()) | 51 default: |
| 44 chunkStyle |= SVGTextChunk::RightToLeftText; | 52 ASSERT_NOT_REACHED(); |
| 45 | |
| 46 // Handle 'writing-mode' property. | |
| 47 if (svgStyle.isVerticalWritingMode()) | |
| 48 chunkStyle |= SVGTextChunk::VerticalText; | |
| 49 | |
| 50 // Handle 'text-anchor' property. | |
| 51 switch (svgStyle.textAnchor()) { | |
| 52 case TA_START: | 53 case TA_START: |
| 53 break; | 54 return !isLTR; |
| 54 case TA_MIDDLE: | 55 case TA_MIDDLE: |
| 55 chunkStyle |= SVGTextChunk::MiddleAnchor; | 56 return true; |
| 56 break; | |
| 57 case TA_END: | 57 case TA_END: |
| 58 chunkStyle |= SVGTextChunk::EndAnchor; | 58 return isLTR; |
| 59 break; | |
| 60 } | 59 } |
| 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 } | 60 } |
| 85 | 61 |
| 86 class ChunkLengthAccumulator { | 62 class ChunkLengthAccumulator { |
| 87 public: | 63 public: |
| 88 ChunkLengthAccumulator(bool isVertical) | 64 ChunkLengthAccumulator(bool isVertical) |
| 89 : m_numCharacters(0) | 65 : m_numCharacters(0) |
| 90 , m_length(0) | 66 , m_length(0) |
| 91 , m_isVertical(isVertical) | 67 , m_isVertical(isVertical) |
| 92 { | 68 { |
| 93 } | 69 } |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() | 152 SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
| 177 : SVGTextChunkBuilder() | 153 : SVGTextChunkBuilder() |
| 178 , m_totalLength(0) | 154 , m_totalLength(0) |
| 179 , m_totalCharacters(0) | 155 , m_totalCharacters(0) |
| 180 , m_totalTextAnchorShift(0) | 156 , m_totalTextAnchorShift(0) |
| 181 { | 157 { |
| 182 } | 158 } |
| 183 | 159 |
| 184 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box
ListConstIterator boxEnd) | 160 void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, Box
ListConstIterator boxEnd) |
| 185 { | 161 { |
| 186 SVGTextChunk chunk = createTextChunk(**boxStart); | 162 const ComputedStyle& style = (*boxStart)->layoutObject().styleRef(); |
| 187 | 163 |
| 188 ChunkLengthAccumulator lengthAccumulator(chunk.isVerticalText()); | 164 ChunkLengthAccumulator lengthAccumulator(style.svgStyle().isVerticalWritingM
ode()); |
| 189 lengthAccumulator.processRange(boxStart, boxEnd); | 165 lengthAccumulator.processRange(boxStart, boxEnd); |
| 190 | 166 |
| 191 // Handle text-anchor as additional start offset for text paths. | 167 // Handle text-anchor as additional start offset for text paths. |
| 192 m_totalTextAnchorShift += chunk.calculateTextAnchorShift(lengthAccumulator.l
ength()); | 168 m_totalTextAnchorShift += calculateTextAnchorShift(style, lengthAccumulator.
length()); |
| 193 | 169 |
| 194 m_totalLength += lengthAccumulator.length(); | 170 m_totalLength += lengthAccumulator.length(); |
| 195 m_totalCharacters += lengthAccumulator.numCharacters(); | 171 m_totalCharacters += lengthAccumulator.numCharacters(); |
| 196 } | 172 } |
| 197 | 173 |
| 198 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con
st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) | 174 static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con
st SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) |
| 199 { | 175 { |
| 200 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); | 176 spacingAndGlyphsTransform.translate(fragment.x, fragment.y); |
| 201 | 177 |
| 202 if (isVerticalText) | 178 if (isVerticalText) |
| 203 spacingAndGlyphsTransform.scaleNonUniform(1, scale); | 179 spacingAndGlyphsTransform.scaleNonUniform(1, scale); |
| 204 else | 180 else |
| 205 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); | 181 spacingAndGlyphsTransform.scaleNonUniform(scale, 1); |
| 206 | 182 |
| 207 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); | 183 spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); |
| 208 } | 184 } |
| 209 | 185 |
| 210 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList
ConstIterator boxEnd) | 186 void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxList
ConstIterator boxEnd) |
| 211 { | 187 { |
| 212 SVGTextChunk chunk = createTextChunk(**boxStart); | 188 ASSERT(*boxStart); |
| 213 | 189 |
| 214 bool processTextLength = chunk.hasDesiredTextLength(); | 190 const LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText((*boxSta
rt)->layoutObject()); |
| 215 bool processTextAnchor = chunk.hasTextAnchor(); | 191 const ComputedStyle& style = textLayoutObject.styleRef(); |
| 192 |
| 193 // Handle 'lengthAdjust' property. |
| 194 float desiredTextLength = 0; |
| 195 SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown; |
| 196 if (SVGTextContentElement* textContentElement = SVGTextContentElement::eleme
ntFromLayoutObject(textLayoutObject.parent())) { |
| 197 lengthAdjust = textContentElement->lengthAdjust()->currentValue()->enumV
alue(); |
| 198 |
| 199 SVGLengthContext lengthContext(textContentElement); |
| 200 if (textContentElement->textLengthIsSpecifiedByUser()) |
| 201 desiredTextLength = textContentElement->textLength()->currentValue()
->value(lengthContext); |
| 202 else |
| 203 desiredTextLength = 0; |
| 204 } |
| 205 |
| 206 bool processTextLength = desiredTextLength > 0; |
| 207 bool processTextAnchor = needsTextAnchorAdjustment(style); |
| 216 if (!processTextAnchor && !processTextLength) | 208 if (!processTextAnchor && !processTextLength) |
| 217 return; | 209 return; |
| 218 | 210 |
| 219 bool isVerticalText = chunk.isVerticalText(); | 211 bool isVerticalText = style.svgStyle().isVerticalWritingMode(); |
| 220 | 212 |
| 221 // Calculate absolute length of whole text chunk (starting from text box 'st
art', spanning 'length' text boxes). | 213 // Calculate absolute length of whole text chunk (starting from text box 'st
art', spanning 'length' text boxes). |
| 222 ChunkLengthAccumulator lengthAccumulator(isVerticalText); | 214 ChunkLengthAccumulator lengthAccumulator(isVerticalText); |
| 223 lengthAccumulator.processRange(boxStart, boxEnd); | 215 lengthAccumulator.processRange(boxStart, boxEnd); |
| 224 | 216 |
| 225 if (processTextLength) { | 217 if (processTextLength) { |
| 226 float chunkLength = lengthAccumulator.length(); | 218 float chunkLength = lengthAccumulator.length(); |
| 227 if (chunk.hasLengthAdjustSpacing()) { | 219 if (lengthAdjust == SVGLengthAdjustSpacing) { |
| 228 float textLengthShift = (chunk.desiredTextLength() - chunkLength) /
lengthAccumulator.numCharacters(); | 220 float textLengthShift = (desiredTextLength - chunkLength) / lengthAc
cumulator.numCharacters(); |
| 229 unsigned atCharacter = 0; | 221 unsigned atCharacter = 0; |
| 230 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 222 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 231 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments()
; | 223 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments()
; |
| 232 if (fragments.isEmpty()) | 224 if (fragments.isEmpty()) |
| 233 continue; | 225 continue; |
| 234 processTextLengthSpacingCorrection(isVerticalText, textLengthShi
ft, fragments, atCharacter); | 226 processTextLengthSpacingCorrection(isVerticalText, textLengthShi
ft, fragments, atCharacter); |
| 235 } | 227 } |
| 236 | 228 |
| 237 // Fragments have been adjusted, we have to recalculate the chunk | 229 // Fragments have been adjusted, we have to recalculate the chunk |
| 238 // length, to be able to apply the text-anchor shift. | 230 // length, to be able to apply the text-anchor shift. |
| 239 if (processTextAnchor) { | 231 if (processTextAnchor) { |
| 240 lengthAccumulator.reset(); | 232 lengthAccumulator.reset(); |
| 241 lengthAccumulator.processRange(boxStart, boxEnd); | 233 lengthAccumulator.processRange(boxStart, boxEnd); |
| 242 } | 234 } |
| 243 } else { | 235 } else { |
| 244 ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); | 236 ASSERT(lengthAdjust == SVGLengthAdjustSpacingAndGlyphs); |
| 245 float textLengthScale = chunk.desiredTextLength() / chunkLength; | 237 float textLengthScale = desiredTextLength / chunkLength; |
| 246 AffineTransform spacingAndGlyphsTransform; | 238 AffineTransform spacingAndGlyphsTransform; |
| 247 | 239 |
| 248 bool foundFirstFragment = false; | 240 bool foundFirstFragment = false; |
| 249 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 241 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 250 SVGInlineTextBox* textBox = *boxIter; | 242 SVGInlineTextBox* textBox = *boxIter; |
| 251 Vector<SVGTextFragment>& fragments = textBox->textFragments(); | 243 Vector<SVGTextFragment>& fragments = textBox->textFragments(); |
| 252 if (fragments.isEmpty()) | 244 if (fragments.isEmpty()) |
| 253 continue; | 245 continue; |
| 254 | 246 |
| 255 if (!foundFirstFragment) { | 247 if (!foundFirstFragment) { |
| 256 foundFirstFragment = true; | 248 foundFirstFragment = true; |
| 257 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca
le, fragments.first(), spacingAndGlyphsTransform); | 249 buildSpacingAndGlyphsTransform(isVerticalText, textLengthSca
le, fragments.first(), spacingAndGlyphsTransform); |
| 258 } | 250 } |
| 259 | 251 |
| 260 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform)
; | 252 m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform)
; |
| 261 } | 253 } |
| 262 } | 254 } |
| 263 } | 255 } |
| 264 | 256 |
| 265 if (!processTextAnchor) | 257 if (!processTextAnchor) |
| 266 return; | 258 return; |
| 267 | 259 |
| 268 float textAnchorShift = chunk.calculateTextAnchorShift(lengthAccumulator.len
gth()); | 260 float textAnchorShift = calculateTextAnchorShift(style, lengthAccumulator.le
ngth()); |
| 269 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { | 261 for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
| 270 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); | 262 Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
| 271 if (fragments.isEmpty()) | 263 if (fragments.isEmpty()) |
| 272 continue; | 264 continue; |
| 273 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); | 265 processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); |
| 274 } | 266 } |
| 275 } | 267 } |
| 276 | 268 |
| 277 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText
, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact
er) | 269 void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText
, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharact
er) |
| 278 { | 270 { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 continue; | 305 continue; |
| 314 | 306 |
| 315 for (SVGTextFragment& fragment : textBox->textFragments()) { | 307 for (SVGTextFragment& fragment : textBox->textFragments()) { |
| 316 ASSERT(fragment.lengthAdjustTransform.isIdentity()); | 308 ASSERT(fragment.lengthAdjustTransform.isIdentity()); |
| 317 fragment.lengthAdjustTransform = textBoxTransformation; | 309 fragment.lengthAdjustTransform = textBoxTransformation; |
| 318 } | 310 } |
| 319 } | 311 } |
| 320 } | 312 } |
| 321 | 313 |
| 322 } | 314 } |
| OLD | NEW |