1 /* 2 * Copyright (C) Research In Motion Limited 2010-2012. 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 22 #include "core/rendering/svg/SVGTextMetricsBuilder.h" 23 24 #include "core/rendering/svg/RenderSVGInline.h" 25 #include "core/rendering/svg/RenderSVGInlineText.h" 26 #include "core/rendering/svg/RenderSVGText.h" 27 #include "core/rendering/svg/SVGTextMetrics.h" 28 #include "platform/fonts/GlyphBuffer.h" 29 #include "platform/fonts/WidthIterator.h" 30 #include "platform/text/BidiCharacterRun.h" 31 #include "platform/text/BidiResolver.h" 32 #include "platform/text/TextDirection.h" 33 #include "platform/text/TextPath.h" 34 #include "platform/text/TextRun.h" 35 #include "platform/text/TextRunIterator.h" 36 #include "wtf/Vector.h" 37 38 namespace WebCore { 39 40 class SVGTextMetricsCalculator { 41 public: 42 SVGTextMetricsCalculator(RenderSVGInlineText*); 43 ~SVGTextMetricsCalculator(); 44 45 SVGTextMetrics computeMetricsForCharacter(unsigned textPosition); 46 unsigned textLength() const { return static_cast<unsigned>(m_run.charactersLength()); } 47 48 bool characterStartsSurrogatePair(unsigned textPosition) const 49 { 50 return U16_IS_LEAD(m_run[textPosition]) && textPosition + 1 < textLength() && U16_IS_TRAIL(m_run[textPosition + 1]); 51 } 52 bool characterIsWhiteSpace(unsigned textPosition) const 53 { 54 return m_run[textPosition] == ' '; 55 } 56 57 private: 58 void setupBidiRuns(); 59 SVGTextMetrics computeMetricsForCharacterSimple(unsigned textPosition); 60 SVGTextMetrics computeMetricsForCharacterComplex(unsigned textPosition); 61 62 RenderSVGInlineText* m_text; 63 BidiCharacterRun* m_bidiRun; 64 TextRun m_run; 65 BidiResolver<TextRunIterator, BidiCharacterRun> m_bidiResolver; 66 bool m_isComplexText; 67 float m_totalWidth; 68 TextDirection m_textDirection; 69 70 // Simple text only. 71 OwnPtr<WidthIterator> m_simpleWidthIterator; 72 }; 73 74 SVGTextMetricsCalculator::SVGTextMetricsCalculator(RenderSVGInlineText* text) 75 : m_text(text) 76 , m_bidiRun(0) 77 , m_run(SVGTextMetrics::constructTextRun(text, 0, text->textLength())) 78 , m_isComplexText(false) 79 , m_totalWidth(0) 80 { 81 const Font& scaledFont = text->scaledFont(); 82 CodePath codePath = scaledFont.codePath(m_run); 83 m_isComplexText = codePath == ComplexPath; 84 m_run.setCharacterScanForCodePath(!m_isComplexText); 85 86 if (!m_isComplexText) 87 m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run)); 88 else 89 setupBidiRuns(); 90 } 91 92 SVGTextMetricsCalculator::~SVGTextMetricsCalculator() 93 { 94 if (m_bidiRun) 95 m_bidiResolver.runs().deleteRuns(); 96 } 97 98 void SVGTextMetricsCalculator::setupBidiRuns() 99 { 100 RenderStyle* style = m_text->style(); 101 m_textDirection = style->direction(); 102 if (isOverride(style->unicodeBidi())) 103 return; 104 105 BidiStatus status(LTR, false); 106 status.last = status.lastStrong = WTF::Unicode::OtherNeutral; 107 m_bidiResolver.setStatus(status); 108 m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)); 109 const bool hardLineBreak = false; 110 const bool reorderRuns = false; 111 m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns); 112 BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); 113 m_bidiRun = bidiRuns.firstRun(); 114 } 115 116 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterSimple(unsigned textPosition) 117 { 118 GlyphBuffer glyphBuffer; 119 unsigned metricsLength = m_simpleWidthIterator->advance(textPosition + 1, &glyphBuffer); 120 if (!metricsLength) 121 return SVGTextMetrics(); 122 123 float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth; 124 m_totalWidth = m_simpleWidthIterator->runWidthSoFar(); 125 126 Glyph glyphId = glyphBuffer.glyphAt(0); 127 return SVGTextMetrics(m_text, textPosition, metricsLength, currentWidth, glyphId); 128 } 129 130 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition) 131 { 132 unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1; 133 SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection); 134 ASSERT(metrics.length() == metricsLength); 135 136 unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0; 137 ASSERT(startPosition <= textPosition); 138 SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection); 139 // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken 140 // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping. 141 // So whenever currentWidth != currentMetrics.width(), we are processing a text run whose length is 142 // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated. 143 float currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; 144 if (currentWidth != metrics.width()) 145 metrics.setWidth(currentWidth); 146 147 m_totalWidth = complexStartToCurrentMetrics.width(); 148 return metrics; 149 } 150 151 SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition) 152 { 153 if (m_bidiRun) { 154 if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) { 155 m_bidiRun = m_bidiRun->next(); 156 // New BiDi run means new reference position for measurements, so reset |m_totalWidth|. 157 m_totalWidth = 0; 158 } 159 ASSERT(m_bidiRun); 160 ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop()); 161 m_textDirection = m_bidiRun->direction(); 162 } 163 164 if (m_isComplexText) 165 return computeMetricsForCharacterComplex(textPosition); 166 167 return computeMetricsForCharacterSimple(textPosition); 168 } 169 170 struct MeasureTextData { 171 MeasureTextData(SVGCharacterDataMap* characterDataMap) 172 : allCharactersMap(characterDataMap) 173 , lastCharacterWasWhiteSpace(true) 174 , valueListPosition(0) 175 { 176 } 177 178 SVGCharacterDataMap* allCharactersMap; 179 bool lastCharacterWasWhiteSpace; 180 unsigned valueListPosition; 181 }; 182 183 static void measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data, bool processRenderer) 184 { 185 ASSERT(text); 186 187 SVGTextLayoutAttributes* attributes = text->layoutAttributes(); 188 Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues(); 189 if (processRenderer) { 190 if (data->allCharactersMap) 191 attributes->clear(); 192 else 193 textMetricsValues->clear(); 194 } 195 196 SVGTextMetricsCalculator calculator(text); 197 bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; 198 unsigned surrogatePairCharacters = 0; 199 unsigned skippedCharacters = 0; 200 unsigned textPosition = 0; 201 unsigned textLength = calculator.textLength(); 202 203 SVGTextMetrics currentMetrics; 204 for (; textPosition < textLength; textPosition += currentMetrics.length()) { 205 currentMetrics = calculator.computeMetricsForCharacter(textPosition); 206 if (!currentMetrics.length()) 207 break; 208 209 bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPosition); 210 if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterWasWhiteSpace) { 211 if (processRenderer) 212 textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); 213 if (data->allCharactersMap) 214 skippedCharacters += currentMetrics.length(); 215 continue; 216 } 217 218 if (processRenderer) { 219 if (data->allCharactersMap) { 220 const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + textPosition - skippedCharacters - surrogatePairCharacters + 1); 221 if (it != data->allCharactersMap->end()) 222 attributes->characterDataMap().set(textPosition + 1, it->value); 223 } 224 textMetricsValues->append(currentMetrics); 225 } 226 227 if (data->allCharactersMap && calculator.characterStartsSurrogatePair(textPosition)) 228 surrogatePairCharacters++; 229 230 data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; 231 } 232 233 if (!data->allCharactersMap) 234 return; 235 236 data->valueListPosition += textPosition - skippedCharacters; 237 } 238 239 static void walkTree(RenderSVGText* start, RenderSVGInlineText* stopAtLeaf, MeasureTextData* data) 240 { 241 RenderObject* child = start->firstChild(); 242 while (child) { 243 if (child->isSVGInlineText()) { 244 RenderSVGInlineText* text = toRenderSVGInlineText(child); 245 measureTextRenderer(text, data, !stopAtLeaf || stopAtLeaf == text); 246 if (stopAtLeaf && stopAtLeaf == text) 247 return; 248 } else if (child->isSVGInline()) { 249 // Visit children of text content elements. 250 if (RenderObject* inlineChild = toRenderSVGInline(child)->firstChild()) { 251 child = inlineChild; 252 continue; 253 } 254 } 255 child = child->nextInPreOrderAfterChildren(start); 256 } 257 } 258 259 void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text) 260 { 261 ASSERT(text); 262 263 RenderSVGText* textRoot = RenderSVGText::locateRenderSVGTextAncestor(text); 264 if (!textRoot) 265 return; 266 267 MeasureTextData data(0); 268 walkTree(textRoot, text, &data); 269 } 270 271 void SVGTextMetricsBuilder::buildMetricsAndLayoutAttributes(RenderSVGText* textRoot, RenderSVGInlineText* stopAtLeaf, SVGCharacterDataMap& allCharactersMap) 272 { 273 ASSERT(textRoot); 274 MeasureTextData data(&allCharactersMap); 275 walkTree(textRoot, stopAtLeaf, &data); 276 } 277 278 } 279