| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 Google Inc. All rights reserved. | 2 * Copyright (c) 2012 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. | 3 * Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "platform/fonts/shaping/ShapeResult.h" | 32 #include "platform/fonts/shaping/ShapeResult.h" |
| 33 | 33 |
| 34 #include "platform/fonts/Font.h" | 34 #include "platform/fonts/Font.h" |
| 35 #include "platform/fonts/GlyphBuffer.h" | |
| 36 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" | 35 #include "platform/fonts/shaping/ShapeResultInlineHeaders.h" |
| 37 #include "platform/text/TextBreakIterator.h" | |
| 38 | 36 |
| 39 namespace blink { | 37 namespace blink { |
| 40 | 38 |
| 41 float ShapeResult::RunInfo::xPositionForVisualOffset(unsigned offset) const | 39 float ShapeResult::RunInfo::xPositionForVisualOffset(unsigned offset) const |
| 42 { | 40 { |
| 43 ASSERT(offset < m_numCharacters); | 41 ASSERT(offset < m_numCharacters); |
| 44 if (rtl()) | 42 if (rtl()) |
| 45 offset = m_numCharacters - offset - 1; | 43 offset = m_numCharacters - offset - 1; |
| 46 return xPositionForOffset(offset); | 44 return xPositionForOffset(offset); |
| 47 } | 45 } |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 123 , m_numGlyphs(0) | 121 , m_numGlyphs(0) |
| 124 , m_direction(direction) | 122 , m_direction(direction) |
| 125 , m_hasVerticalOffsets(0) | 123 , m_hasVerticalOffsets(0) |
| 126 { | 124 { |
| 127 } | 125 } |
| 128 | 126 |
| 129 ShapeResult::~ShapeResult() | 127 ShapeResult::~ShapeResult() |
| 130 { | 128 { |
| 131 } | 129 } |
| 132 | 130 |
| 133 static inline void addGlyphToBuffer(GlyphBuffer* glyphBuffer, float advance, | |
| 134 hb_direction_t direction, const SimpleFontData* fontData, | |
| 135 const HarfBuzzRunGlyphData& glyphData) | |
| 136 { | |
| 137 FloatPoint startOffset = HB_DIRECTION_IS_HORIZONTAL(direction) | |
| 138 ? FloatPoint(advance, 0) | |
| 139 : FloatPoint(0, advance); | |
| 140 glyphBuffer->add(glyphData.glyph, fontData, startOffset + glyphData.offset); | |
| 141 } | |
| 142 | |
| 143 template<TextDirection direction> | |
| 144 float ShapeResult::fillGlyphBufferForRun(GlyphBuffer* glyphBuffer, | |
| 145 const RunInfo* run, float initialAdvance, unsigned from, unsigned to, | |
| 146 unsigned runOffset) | |
| 147 { | |
| 148 if (!run) | |
| 149 return 0; | |
| 150 float advanceSoFar = initialAdvance; | |
| 151 const unsigned numGlyphs = run->m_glyphData.size(); | |
| 152 for (unsigned i = 0; i < numGlyphs; ++i) { | |
| 153 const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; | |
| 154 uint16_t currentCharacterIndex = run->m_startIndex + | |
| 155 glyphData.characterIndex + runOffset; | |
| 156 if ((direction == RTL && currentCharacterIndex >= to) | |
| 157 || (direction == LTR && currentCharacterIndex < from)) { | |
| 158 advanceSoFar += glyphData.advance; | |
| 159 } else if ((direction == RTL && currentCharacterIndex >= from) | |
| 160 || (direction == LTR && currentCharacterIndex < to)) { | |
| 161 addGlyphToBuffer(glyphBuffer, advanceSoFar, run->m_direction, | |
| 162 run->m_fontData.get(), glyphData); | |
| 163 advanceSoFar += glyphData.advance; | |
| 164 } | |
| 165 } | |
| 166 return advanceSoFar - initialAdvance; | |
| 167 } | |
| 168 | |
| 169 static inline unsigned countGraphemesInCluster(const UChar* str, | |
| 170 unsigned strLength, uint16_t startIndex, uint16_t endIndex) | |
| 171 { | |
| 172 if (startIndex > endIndex) { | |
| 173 uint16_t tempIndex = startIndex; | |
| 174 startIndex = endIndex; | |
| 175 endIndex = tempIndex; | |
| 176 } | |
| 177 uint16_t length = endIndex - startIndex; | |
| 178 ASSERT(static_cast<unsigned>(startIndex + length) <= strLength); | |
| 179 TextBreakIterator* cursorPosIterator = cursorMovementIterator(&str[startInde
x], length); | |
| 180 | |
| 181 int cursorPos = cursorPosIterator->current(); | |
| 182 int numGraphemes = -1; | |
| 183 while (0 <= cursorPos) { | |
| 184 cursorPos = cursorPosIterator->next(); | |
| 185 numGraphemes++; | |
| 186 } | |
| 187 return std::max(0, numGraphemes); | |
| 188 } | |
| 189 | |
| 190 static inline void addEmphasisMark(GlyphBuffer* buffer, | |
| 191 const GlyphData* emphasisData, FloatPoint glyphCenter, | |
| 192 float midGlyphOffset) | |
| 193 { | |
| 194 ASSERT(buffer); | |
| 195 ASSERT(emphasisData); | |
| 196 | |
| 197 const SimpleFontData* emphasisFontData = emphasisData->fontData; | |
| 198 ASSERT(emphasisFontData); | |
| 199 | |
| 200 bool isVertical = emphasisFontData->platformData().isVerticalAnyUpright() | |
| 201 && emphasisFontData->verticalData(); | |
| 202 | |
| 203 if (!isVertical) { | |
| 204 buffer->add(emphasisData->glyph, emphasisFontData, | |
| 205 midGlyphOffset - glyphCenter.x()); | |
| 206 } else { | |
| 207 buffer->add(emphasisData->glyph, emphasisFontData, | |
| 208 FloatPoint(-glyphCenter.x(), midGlyphOffset - glyphCenter.y())); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, | |
| 213 const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, | |
| 214 float initialAdvance, unsigned from, unsigned to, unsigned runOffset) | |
| 215 { | |
| 216 if (!run) | |
| 217 return 0; | |
| 218 | |
| 219 unsigned graphemesInCluster = 1; | |
| 220 float clusterAdvance = 0; | |
| 221 | |
| 222 FloatPoint glyphCenter = emphasisData->fontData-> | |
| 223 boundsForGlyph(emphasisData->glyph).center(); | |
| 224 | |
| 225 TextDirection direction = textRun.direction(); | |
| 226 | |
| 227 // A "cluster" in this context means a cluster as it is used by HarfBuzz: | |
| 228 // The minimal group of characters and corresponding glyphs, that cannot be
broken | |
| 229 // down further from a text shaping point of view. | |
| 230 // A cluster can contain multiple glyphs and grapheme clusters, with mutuall
y | |
| 231 // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clu
sters, | |
| 232 // then linearly split the sum of corresponding glyph advances by the number
of | |
| 233 // grapheme clusters in order to find positions for emphasis mark drawing. | |
| 234 uint16_t clusterStart = static_cast<uint16_t>(direction == RTL | |
| 235 ? run->m_startIndex + run->m_numCharacters + runOffset | |
| 236 : run->glyphToCharacterIndex(0) + runOffset); | |
| 237 | |
| 238 float advanceSoFar = initialAdvance; | |
| 239 const unsigned numGlyphs = run->m_glyphData.size(); | |
| 240 for (unsigned i = 0; i < numGlyphs; ++i) { | |
| 241 const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; | |
| 242 uint16_t currentCharacterIndex = run->m_startIndex + glyphData.character
Index + runOffset; | |
| 243 bool isRunEnd = (i + 1 == numGlyphs); | |
| 244 bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) + ru
nOffset != currentCharacterIndex); | |
| 245 | |
| 246 if ((direction == RTL && currentCharacterIndex >= to) || (direction != R
TL && currentCharacterIndex < from)) { | |
| 247 advanceSoFar += glyphData.advance; | |
| 248 direction == RTL ? --clusterStart : ++clusterStart; | |
| 249 continue; | |
| 250 } | |
| 251 | |
| 252 clusterAdvance += glyphData.advance; | |
| 253 | |
| 254 if (textRun.is8Bit()) { | |
| 255 float glyphAdvanceX = glyphData.advance; | |
| 256 if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex]
)) { | |
| 257 addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceS
oFar + glyphAdvanceX / 2); | |
| 258 } | |
| 259 advanceSoFar += glyphAdvanceX; | |
| 260 } else if (isClusterEnd) { | |
| 261 uint16_t clusterEnd; | |
| 262 if (direction == RTL) | |
| 263 clusterEnd = currentCharacterIndex; | |
| 264 else | |
| 265 clusterEnd = static_cast<uint16_t>(isRunEnd ? run->m_startIndex
+ run->m_numCharacters + runOffset : run->glyphToCharacterIndex(i + 1) + runOffs
et); | |
| 266 | |
| 267 graphemesInCluster = countGraphemesInCluster(textRun.characters16(),
textRun.charactersLength(), clusterStart, clusterEnd); | |
| 268 if (!graphemesInCluster || !clusterAdvance) | |
| 269 continue; | |
| 270 | |
| 271 float glyphAdvanceX = clusterAdvance / graphemesInCluster; | |
| 272 for (unsigned j = 0; j < graphemesInCluster; ++j) { | |
| 273 // Do not put emphasis marks on space, separator, and control ch
aracters. | |
| 274 if (Character::canReceiveTextEmphasis(textRun[currentCharacterIn
dex])) | |
| 275 addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, adva
nceSoFar + glyphAdvanceX / 2); | |
| 276 advanceSoFar += glyphAdvanceX; | |
| 277 } | |
| 278 clusterStart = clusterEnd; | |
| 279 clusterAdvance = 0; | |
| 280 } | |
| 281 } | |
| 282 return advanceSoFar - initialAdvance; | |
| 283 } | |
| 284 | |
| 285 float ShapeResult::fillFastHorizontalGlyphBuffer(const ShapeResultBuffer& result
sBuffer, | |
| 286 GlyphBuffer* glyphBuffer, TextDirection dir) | |
| 287 { | |
| 288 ASSERT(!resultsBuffer.hasVerticalOffsets()); | |
| 289 | |
| 290 const auto& results = resultsBuffer.results(); | |
| 291 float advance = 0; | |
| 292 | |
| 293 for (unsigned i = 0; i < results.size(); ++i) { | |
| 294 const auto& wordResult = | |
| 295 isLeftToRightDirection(dir) ? results[i] : results[results.size() -
1 - i]; | |
| 296 ASSERT(!wordResult->hasVerticalOffsets()); | |
| 297 | |
| 298 for (const auto& run : wordResult->m_runs) { | |
| 299 ASSERT(run); | |
| 300 ASSERT(HB_DIRECTION_IS_HORIZONTAL(run->m_direction)); | |
| 301 | |
| 302 for (const auto& glyphData : run->m_glyphData) { | |
| 303 ASSERT(!glyphData.offset.height()); | |
| 304 | |
| 305 glyphBuffer->add(glyphData.glyph, run->m_fontData.get(), | |
| 306 advance + glyphData.offset.width()); | |
| 307 advance += glyphData.advance; | |
| 308 } | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 ASSERT(!glyphBuffer->hasVerticalOffsets()); | |
| 313 | |
| 314 return advance; | |
| 315 } | |
| 316 | |
| 317 float ShapeResult::fillGlyphBuffer(const ShapeResultBuffer& resultsBuffer, | |
| 318 GlyphBuffer* glyphBuffer, const TextRun& textRun, | |
| 319 unsigned from, unsigned to) | |
| 320 { | |
| 321 // Fast path: full run with no vertical offsets | |
| 322 if (!from && to == static_cast<unsigned>(textRun.length()) && !resultsBuffer
.hasVerticalOffsets()) | |
| 323 return fillFastHorizontalGlyphBuffer(resultsBuffer, glyphBuffer, textRun
.direction()); | |
| 324 | |
| 325 const auto& results = resultsBuffer.results(); | |
| 326 float advance = 0; | |
| 327 | |
| 328 if (textRun.rtl()) { | |
| 329 unsigned wordOffset = textRun.length(); | |
| 330 for (unsigned j = 0; j < results.size(); j++) { | |
| 331 unsigned resolvedIndex = results.size() - 1 - j; | |
| 332 const RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; | |
| 333 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { | |
| 334 advance += wordResult->fillGlyphBufferForRun<RTL>(glyphBuffer, | |
| 335 wordResult->m_runs[i].get(), advance, from, to, | |
| 336 wordOffset - wordResult->numCharacters()); | |
| 337 } | |
| 338 wordOffset -= wordResult->numCharacters(); | |
| 339 } | |
| 340 } else { | |
| 341 unsigned wordOffset = 0; | |
| 342 for (unsigned j = 0; j < results.size(); j++) { | |
| 343 const RefPtr<ShapeResult>& wordResult = results[j]; | |
| 344 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { | |
| 345 advance += wordResult->fillGlyphBufferForRun<LTR>(glyphBuffer, | |
| 346 wordResult->m_runs[i].get(), advance, from, to, wordOffset); | |
| 347 } | |
| 348 wordOffset += wordResult->numCharacters(); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 return advance; | |
| 353 } | |
| 354 | |
| 355 float ShapeResult::fillGlyphBufferForTextEmphasis(const ShapeResultBuffer& resul
tsBuffer, | |
| 356 GlyphBuffer* glyphBuffer, const TextRun& textRun, const GlyphData* emphasisD
ata, | |
| 357 unsigned from, unsigned to) | |
| 358 { | |
| 359 const auto& results = resultsBuffer.results(); | |
| 360 float advance = 0; | |
| 361 unsigned wordOffset = textRun.rtl() ? textRun.length() : 0; | |
| 362 | |
| 363 for (unsigned j = 0; j < results.size(); j++) { | |
| 364 unsigned resolvedIndex = textRun.rtl() ? results.size() - 1 - j : j; | |
| 365 const RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; | |
| 366 for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { | |
| 367 unsigned resolvedOffset = wordOffset - | |
| 368 (textRun.rtl() ? wordResult->numCharacters() : 0); | |
| 369 advance += wordResult->fillGlyphBufferForTextEmphasisRun( | |
| 370 glyphBuffer, wordResult->m_runs[i].get(), textRun, emphasisData, | |
| 371 advance, from, to, resolvedOffset); | |
| 372 } | |
| 373 wordOffset += wordResult->numCharacters() * (textRun.rtl() ? -1 : 1); | |
| 374 } | |
| 375 | |
| 376 return advance; | |
| 377 } | |
| 378 | |
| 379 FloatRect ShapeResult::selectionRect(const ShapeResultBuffer& resultsBuffer, | |
| 380 TextDirection direction, float totalWidth, const FloatPoint& point, | |
| 381 int height, unsigned absoluteFrom, unsigned absoluteTo) | |
| 382 { | |
| 383 const auto& results = resultsBuffer.results(); | |
| 384 float currentX = 0; | |
| 385 float fromX = 0; | |
| 386 float toX = 0; | |
| 387 bool foundFromX = false; | |
| 388 bool foundToX = false; | |
| 389 | |
| 390 if (direction == RTL) | |
| 391 currentX = totalWidth; | |
| 392 | |
| 393 // The absoluteFrom and absoluteTo arguments represent the start/end offset | |
| 394 // for the entire run, from/to are continuously updated to be relative to | |
| 395 // the current word (ShapeResult instance). | |
| 396 int from = absoluteFrom; | |
| 397 int to = absoluteTo; | |
| 398 | |
| 399 unsigned totalNumCharacters = 0; | |
| 400 for (unsigned j = 0; j < results.size(); j++) { | |
| 401 const RefPtr<ShapeResult> result = results[j]; | |
| 402 if (direction == RTL) { | |
| 403 // Convert logical offsets to visual offsets, because results are in | |
| 404 // logical order while runs are in visual order. | |
| 405 if (!foundFromX && from >= 0 && static_cast<unsigned>(from) < result
->numCharacters()) | |
| 406 from = result->numCharacters() - from - 1; | |
| 407 if (!foundToX && to >= 0 && static_cast<unsigned>(to) < result->numC
haracters()) | |
| 408 to = result->numCharacters() - to - 1; | |
| 409 currentX -= result->width(); | |
| 410 } | |
| 411 for (unsigned i = 0; i < result->m_runs.size(); i++) { | |
| 412 if (!result->m_runs[i]) | |
| 413 continue; | |
| 414 ASSERT((direction == RTL) == result->m_runs[i]->rtl()); | |
| 415 int numCharacters = result->m_runs[i]->m_numCharacters; | |
| 416 if (!foundFromX && from >= 0 && from < numCharacters) { | |
| 417 fromX = result->m_runs[i]->xPositionForVisualOffset(from) + curr
entX; | |
| 418 foundFromX = true; | |
| 419 } else { | |
| 420 from -= numCharacters; | |
| 421 } | |
| 422 | |
| 423 if (!foundToX && to >= 0 && to < numCharacters) { | |
| 424 toX = result->m_runs[i]->xPositionForVisualOffset(to) + currentX
; | |
| 425 foundToX = true; | |
| 426 } else { | |
| 427 to -= numCharacters; | |
| 428 } | |
| 429 | |
| 430 if (foundFromX && foundToX) | |
| 431 break; | |
| 432 currentX += result->m_runs[i]->m_width; | |
| 433 } | |
| 434 if (direction == RTL) | |
| 435 currentX -= result->width(); | |
| 436 totalNumCharacters += result->numCharacters(); | |
| 437 } | |
| 438 | |
| 439 // The position in question might be just after the text. | |
| 440 if (!foundFromX && absoluteFrom == totalNumCharacters) { | |
| 441 fromX = direction == RTL ? 0 : totalWidth; | |
| 442 foundFromX = true; | |
| 443 } | |
| 444 if (!foundToX && absoluteTo == totalNumCharacters) { | |
| 445 toX = direction == RTL ? 0 : totalWidth; | |
| 446 foundToX = true; | |
| 447 } | |
| 448 if (!foundFromX) | |
| 449 fromX = 0; | |
| 450 if (!foundToX) | |
| 451 toX = direction == RTL ? 0 : totalWidth; | |
| 452 | |
| 453 // None of our runs is part of the selection, possibly invalid arguments. | |
| 454 if (!foundToX && !foundFromX) | |
| 455 fromX = toX = 0; | |
| 456 if (fromX < toX) | |
| 457 return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); | |
| 458 return FloatRect(point.x() + toX, point.y(), fromX - toX, height); | |
| 459 } | |
| 460 | |
| 461 size_t ShapeResult::byteSize() | 131 size_t ShapeResult::byteSize() |
| 462 { | 132 { |
| 463 size_t selfByteSize = sizeof(this); | 133 size_t selfByteSize = sizeof(this); |
| 464 for (unsigned i = 0; i < m_runs.size(); ++i) { | 134 for (unsigned i = 0; i < m_runs.size(); ++i) { |
| 465 selfByteSize += m_runs[i]->byteSize(); | 135 selfByteSize += m_runs[i]->byteSize(); |
| 466 } | 136 } |
| 467 return selfByteSize; | 137 return selfByteSize; |
| 468 } | 138 } |
| 469 | 139 |
| 470 int ShapeResult::offsetForPosition(const ShapeResultBuffer& resultsBuffer, | 140 int ShapeResult::offsetForPosition(float targetX) const |
| 471 const TextRun& run, float targetX) | |
| 472 { | |
| 473 const auto& results = resultsBuffer.results(); | |
| 474 unsigned totalOffset; | |
| 475 if (run.rtl()) { | |
| 476 totalOffset = run.length(); | |
| 477 for (unsigned i = results.size(); i; --i) { | |
| 478 const RefPtr<ShapeResult>& wordResult = results[i - 1]; | |
| 479 if (!wordResult) | |
| 480 continue; | |
| 481 totalOffset -= wordResult->numCharacters(); | |
| 482 if (targetX >= 0 && targetX <= wordResult->width()) { | |
| 483 int offsetForWord = wordResult->offsetForPosition(targetX); | |
| 484 return totalOffset + offsetForWord; | |
| 485 } | |
| 486 targetX -= wordResult->width(); | |
| 487 } | |
| 488 } else { | |
| 489 totalOffset = 0; | |
| 490 for (const auto& wordResult : results) { | |
| 491 if (!wordResult) | |
| 492 continue; | |
| 493 int offsetForWord = wordResult->offsetForPosition(targetX); | |
| 494 ASSERT(offsetForWord >= 0); | |
| 495 totalOffset += offsetForWord; | |
| 496 if (targetX >= 0 && targetX <= wordResult->width()) | |
| 497 return totalOffset; | |
| 498 targetX -= wordResult->width(); | |
| 499 } | |
| 500 } | |
| 501 return totalOffset; | |
| 502 } | |
| 503 | |
| 504 int ShapeResult::offsetForPosition(float targetX) | |
| 505 { | 141 { |
| 506 int charactersSoFar = 0; | 142 int charactersSoFar = 0; |
| 507 float currentX = 0; | 143 float currentX = 0; |
| 508 | 144 |
| 509 if (m_direction == RTL) { | 145 if (m_direction == RTL) { |
| 510 charactersSoFar = m_numCharacters; | 146 charactersSoFar = m_numCharacters; |
| 511 for (unsigned i = 0; i < m_runs.size(); ++i) { | 147 for (unsigned i = 0; i < m_runs.size(); ++i) { |
| 512 if (!m_runs[i]) | 148 if (!m_runs[i]) |
| 513 continue; | 149 continue; |
| 514 charactersSoFar -= m_runs[i]->m_numCharacters; | 150 charactersSoFar -= m_runs[i]->m_numCharacters; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 545 ASSERT(m_primaryFont); | 181 ASSERT(m_primaryFont); |
| 546 for (unsigned i = 0; i < m_runs.size(); ++i) { | 182 for (unsigned i = 0; i < m_runs.size(); ++i) { |
| 547 if (m_runs[i] && m_runs[i]->m_fontData != m_primaryFont | 183 if (m_runs[i] && m_runs[i]->m_fontData != m_primaryFont |
| 548 && !m_runs[i]->m_fontData->isTextOrientationFallbackOf(m_primaryFont
.get())) { | 184 && !m_runs[i]->m_fontData->isTextOrientationFallbackOf(m_primaryFont
.get())) { |
| 549 fallback->add(m_runs[i]->m_fontData.get()); | 185 fallback->add(m_runs[i]->m_fontData.get()); |
| 550 } | 186 } |
| 551 } | 187 } |
| 552 } | 188 } |
| 553 | 189 |
| 554 } // namespace blink | 190 } // namespace blink |
| OLD | NEW |