| Index: Source/platform/fonts/mac/ComplexTextController.cpp
|
| diff --git a/Source/platform/fonts/mac/ComplexTextController.cpp b/Source/platform/fonts/mac/ComplexTextController.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b3048b5c13eaa898f2fd4f271dbd44a426b2081e
|
| --- /dev/null
|
| +++ b/Source/platform/fonts/mac/ComplexTextController.cpp
|
| @@ -0,0 +1,614 @@
|
| +/*
|
| + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
|
| + *
|
| + * Redistribution and use in source and binary forms, with or without
|
| + * modification, are permitted provided that the following conditions
|
| + * are met:
|
| + * 1. Redistributions of source code must retain the above copyright
|
| + * notice, this list of conditions and the following disclaimer.
|
| + * 2. Redistributions in binary form must reproduce the above copyright
|
| + * notice, this list of conditions and the following disclaimer in the
|
| + * documentation and/or other materials provided with the distribution.
|
| + *
|
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
|
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
| + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
| + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| + */
|
| +
|
| +#include "config.h"
|
| +#include "platform/fonts/mac/ComplexTextController.h"
|
| +
|
| +#include "platform/fonts/Character.h"
|
| +#include "platform/fonts/Font.h"
|
| +#include "platform/fonts/GlyphBuffer.h"
|
| +#include "platform/geometry/FloatSize.h"
|
| +#include "platform/text/TextBreakIterator.h"
|
| +#include "platform/text/TextRun.h"
|
| +#include "wtf/StdLibExtras.h"
|
| +#include "wtf/unicode/CharacterNames.h"
|
| +#include <ApplicationServices/ApplicationServices.h>
|
| +
|
| +namespace blink {
|
| +
|
| +ComplexTextController::ComplexTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection, HashSet<const SimpleFontData*>* fallbackFonts, bool forTextEmphasis)
|
| + : m_font(*font)
|
| + , m_run(run)
|
| + , m_isLTROnly(true)
|
| + , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection)
|
| + , m_forTextEmphasis(forTextEmphasis)
|
| + , m_currentCharacter(0)
|
| + , m_end(run.length())
|
| + , m_totalWidth(0)
|
| + , m_runWidthSoFar(0)
|
| + , m_numGlyphsSoFar(0)
|
| + , m_currentRun(0)
|
| + , m_glyphInCurrentRun(0)
|
| + , m_characterInCurrentGlyph(0)
|
| + , m_expansion(run.expansion())
|
| + , m_leadingExpansion(0)
|
| + , m_afterExpansion(!run.allowsLeadingExpansion())
|
| + , m_fallbackFonts(fallbackFonts)
|
| + , m_minGlyphBoundingBoxX(std::numeric_limits<float>::max())
|
| + , m_maxGlyphBoundingBoxX(std::numeric_limits<float>::min())
|
| + , m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
|
| + , m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
|
| +{
|
| + if (!m_expansion)
|
| + m_expansionPerOpportunity = 0;
|
| + else {
|
| + bool isAfterExpansion = m_afterExpansion;
|
| + unsigned expansionOpportunityCount;
|
| + if (m_run.is8Bit())
|
| + expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters8(), m_end, m_run.direction(), isAfterExpansion);
|
| + else
|
| + expansionOpportunityCount = Character::expansionOpportunityCount(m_run.characters16(), m_end, m_run.direction(), isAfterExpansion);
|
| + if (isAfterExpansion && !m_run.allowsTrailingExpansion())
|
| + expansionOpportunityCount--;
|
| +
|
| + if (!expansionOpportunityCount)
|
| + m_expansionPerOpportunity = 0;
|
| + else
|
| + m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
|
| + }
|
| +
|
| + collectComplexTextRuns();
|
| + adjustGlyphsAndAdvances();
|
| +
|
| + if (!m_isLTROnly) {
|
| + m_runIndices.reserveInitialCapacity(m_complexTextRuns.size());
|
| +
|
| + m_glyphCountFromStartToIndex.reserveInitialCapacity(m_complexTextRuns.size());
|
| + unsigned glyphCountSoFar = 0;
|
| + for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
|
| + m_glyphCountFromStartToIndex.uncheckedAppend(glyphCountSoFar);
|
| + glyphCountSoFar += m_complexTextRuns[i]->glyphCount();
|
| + }
|
| + }
|
| +
|
| + m_runWidthSoFar = m_leadingExpansion;
|
| +}
|
| +
|
| +int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs)
|
| +{
|
| + if (h >= m_totalWidth)
|
| + return m_run.ltr() ? m_end : 0;
|
| +
|
| + h -= m_leadingExpansion;
|
| + if (h < 0)
|
| + return m_run.ltr() ? 0 : m_end;
|
| +
|
| + CGFloat x = h;
|
| +
|
| + size_t runCount = m_complexTextRuns.size();
|
| + size_t offsetIntoAdjustedGlyphs = 0;
|
| +
|
| + for (size_t r = 0; r < runCount; ++r) {
|
| + const ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
|
| + for (unsigned j = 0; j < complexTextRun.glyphCount(); ++j) {
|
| + CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width;
|
| + if (x < adjustedAdvance) {
|
| + CFIndex hitGlyphStart = complexTextRun.indexAt(j);
|
| + CFIndex hitGlyphEnd;
|
| + if (m_run.ltr())
|
| + hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
|
| + else
|
| + hitGlyphEnd = std::max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd()));
|
| +
|
| + // FIXME: Instead of dividing the glyph's advance equally between the characters, this
|
| + // could use the glyph's "ligature carets". However, there is no Core Text API to get the
|
| + // ligature carets.
|
| + CFIndex hitIndex = hitGlyphStart + (hitGlyphEnd - hitGlyphStart) * (m_run.ltr() ? x / adjustedAdvance : 1 - x / adjustedAdvance);
|
| + int stringLength = complexTextRun.stringLength();
|
| + TextBreakIterator* cursorPositionIterator = cursorMovementIterator(complexTextRun.characters(), stringLength);
|
| + int clusterStart;
|
| + if (cursorPositionIterator->isBoundary(hitIndex))
|
| + clusterStart = hitIndex;
|
| + else {
|
| + clusterStart = cursorPositionIterator->preceding(hitIndex);
|
| + if (clusterStart == TextBreakDone)
|
| + clusterStart = 0;
|
| + }
|
| +
|
| + if (!includePartialGlyphs)
|
| + return complexTextRun.stringLocation() + clusterStart;
|
| +
|
| + int clusterEnd = cursorPositionIterator->following(hitIndex);
|
| + if (clusterEnd == TextBreakDone)
|
| + clusterEnd = stringLength;
|
| +
|
| + CGFloat clusterWidth;
|
| + // FIXME: The search stops at the boundaries of complexTextRun. In theory, it should go on into neighboring ComplexTextRuns
|
| + // derived from the same CTLine. In practice, we do not expect there to be more than one CTRun in a CTLine, as no
|
| + // reordering and no font fallback should occur within a CTLine.
|
| + if (clusterEnd - clusterStart > 1) {
|
| + clusterWidth = adjustedAdvance;
|
| + int firstGlyphBeforeCluster = j - 1;
|
| + while (firstGlyphBeforeCluster >= 0 && complexTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) {
|
| + CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width;
|
| + clusterWidth += width;
|
| + x += width;
|
| + firstGlyphBeforeCluster--;
|
| + }
|
| + unsigned firstGlyphAfterCluster = j + 1;
|
| + while (firstGlyphAfterCluster < complexTextRun.glyphCount() && complexTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && complexTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) {
|
| + clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width;
|
| + firstGlyphAfterCluster++;
|
| + }
|
| + } else {
|
| + clusterWidth = adjustedAdvance / (hitGlyphEnd - hitGlyphStart);
|
| + x -= clusterWidth * (m_run.ltr() ? hitIndex - hitGlyphStart : hitGlyphEnd - hitIndex - 1);
|
| + }
|
| + if (x <= clusterWidth / 2)
|
| + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd);
|
| + else
|
| + return complexTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart);
|
| + }
|
| + x -= adjustedAdvance;
|
| + }
|
| + offsetIntoAdjustedGlyphs += complexTextRun.glyphCount();
|
| + }
|
| +
|
| + ASSERT_NOT_REACHED();
|
| + return 0;
|
| +}
|
| +
|
| +static bool advanceByCombiningCharacterSequence(const UChar*& iterator, const UChar* end, UChar32& baseCharacter, unsigned& markCount)
|
| +{
|
| + ASSERT(iterator < end);
|
| +
|
| + markCount = 0;
|
| +
|
| + baseCharacter = *iterator++;
|
| +
|
| + if (U16_IS_SURROGATE(baseCharacter)) {
|
| + if (!U16_IS_LEAD(baseCharacter))
|
| + return false;
|
| + if (iterator == end)
|
| + return false;
|
| + UChar trail = *iterator++;
|
| + if (!U16_IS_TRAIL(trail))
|
| + return false;
|
| + baseCharacter = U16_GET_SUPPLEMENTARY(baseCharacter, trail);
|
| + }
|
| +
|
| + // Consume marks.
|
| + while (iterator < end) {
|
| + UChar32 nextCharacter;
|
| + int markLength = 0;
|
| + U16_NEXT(iterator, markLength, end - iterator, nextCharacter);
|
| + if (!(U_GET_GC_MASK(nextCharacter) & U_GC_M_MASK))
|
| + break;
|
| + markCount += markLength;
|
| + iterator += markLength;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void ComplexTextController::collectComplexTextRuns()
|
| +{
|
| + if (!m_end)
|
| + return;
|
| +
|
| + // We break up glyph run generation for the string by FontData.
|
| + const UChar* cp;
|
| +
|
| + if (m_run.is8Bit()) {
|
| + String stringFor8BitRun = String::make16BitFrom8BitSource(m_run.characters8(), m_run.length());
|
| + cp = stringFor8BitRun.characters16();
|
| + m_stringsFor8BitRuns.append(stringFor8BitRun);
|
| + } else
|
| + cp = m_run.characters16();
|
| +
|
| + if (m_font.fontDescription().variant() == FontVariantSmallCaps)
|
| + m_smallCapsBuffer.resize(m_end);
|
| +
|
| + unsigned indexOfFontTransition = 0;
|
| + const UChar* curr = cp;
|
| + const UChar* end = cp + m_end;
|
| +
|
| + const SimpleFontData* fontData;
|
| + bool isMissingGlyph;
|
| + const SimpleFontData* nextFontData;
|
| + bool nextIsMissingGlyph;
|
| +
|
| + unsigned markCount;
|
| + const UChar* sequenceStart = curr;
|
| + UChar32 baseCharacter;
|
| + if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
|
| + return;
|
| +
|
| + UChar uppercaseCharacter = 0;
|
| +
|
| + bool isSmallCaps;
|
| + bool nextIsSmallCaps = m_font.fontDescription().variant() == FontVariantSmallCaps && !(U_GET_GC_MASK(baseCharacter) & U_GC_M_MASK) && (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
|
| +
|
| + if (nextIsSmallCaps) {
|
| + m_smallCapsBuffer[sequenceStart - cp] = uppercaseCharacter;
|
| + for (unsigned i = 0; i < markCount; ++i)
|
| + m_smallCapsBuffer[sequenceStart - cp + i + 1] = sequenceStart[i + 1];
|
| + }
|
| +
|
| + nextIsMissingGlyph = false;
|
| + nextFontData = m_font.fontDataForCombiningCharacterSequence(sequenceStart, curr - sequenceStart, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
|
| + if (!nextFontData)
|
| + nextIsMissingGlyph = true;
|
| +
|
| + while (curr < end) {
|
| + fontData = nextFontData;
|
| + isMissingGlyph = nextIsMissingGlyph;
|
| + isSmallCaps = nextIsSmallCaps;
|
| + int index = curr - cp;
|
| +
|
| + if (!advanceByCombiningCharacterSequence(curr, end, baseCharacter, markCount))
|
| + return;
|
| +
|
| + if (m_font.fontDescription().variant()) {
|
| + nextIsSmallCaps = (uppercaseCharacter = u_toupper(baseCharacter)) != baseCharacter;
|
| + if (nextIsSmallCaps) {
|
| + m_smallCapsBuffer[index] = uppercaseCharacter;
|
| + for (unsigned i = 0; i < markCount; ++i)
|
| + m_smallCapsBuffer[index + i + 1] = cp[index + i + 1];
|
| + }
|
| + }
|
| +
|
| + nextIsMissingGlyph = false;
|
| + if (baseCharacter == zeroWidthJoiner)
|
| + nextFontData = fontData;
|
| + else {
|
| + nextFontData = m_font.fontDataForCombiningCharacterSequence(cp + index, curr - cp - index, nextIsSmallCaps ? SmallCapsVariant : NormalVariant);
|
| + if (!nextFontData)
|
| + nextIsMissingGlyph = true;
|
| + }
|
| +
|
| + if (nextFontData != fontData || nextIsMissingGlyph != isMissingGlyph) {
|
| + int itemStart = static_cast<int>(indexOfFontTransition);
|
| + int itemLength = index - indexOfFontTransition;
|
| + collectComplexTextRunsForCharacters((isSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !isMissingGlyph ? fontData : 0);
|
| + indexOfFontTransition = index;
|
| + }
|
| + }
|
| +
|
| + int itemLength = m_end - indexOfFontTransition;
|
| + if (itemLength) {
|
| + int itemStart = indexOfFontTransition;
|
| + collectComplexTextRunsForCharacters((nextIsSmallCaps ? m_smallCapsBuffer.data() : cp) + itemStart, itemLength, itemStart, !nextIsMissingGlyph ? nextFontData : 0);
|
| + }
|
| +
|
| + if (!m_run.ltr())
|
| + m_complexTextRuns.reverse();
|
| +}
|
| +
|
| +CFIndex ComplexTextController::ComplexTextRun::indexAt(size_t i) const
|
| +{
|
| + return m_coreTextIndices[i];
|
| +}
|
| +
|
| +void ComplexTextController::ComplexTextRun::setIsNonMonotonic()
|
| +{
|
| + ASSERT(m_isMonotonic);
|
| + m_isMonotonic = false;
|
| +
|
| + Vector<bool, 64> mappedIndices(m_stringLength);
|
| + for (size_t i = 0; i < m_glyphCount; ++i) {
|
| + ASSERT(indexAt(i) < static_cast<CFIndex>(m_stringLength));
|
| + mappedIndices[indexAt(i)] = true;
|
| + }
|
| +
|
| + m_glyphEndOffsets.grow(m_glyphCount);
|
| + for (size_t i = 0; i < m_glyphCount; ++i) {
|
| + CFIndex nextMappedIndex = m_indexEnd;
|
| + for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) {
|
| + if (mappedIndices[j]) {
|
| + nextMappedIndex = j;
|
| + break;
|
| + }
|
| + }
|
| + m_glyphEndOffsets[i] = nextMappedIndex;
|
| + }
|
| +}
|
| +
|
| +unsigned ComplexTextController::findNextRunIndex(unsigned runIndex) const
|
| +{
|
| + const unsigned runOffset = stringEnd(*m_complexTextRuns[runIndex]);
|
| +
|
| + // Finds the run with the lowest stringBegin() offset that starts at or
|
| + // after |runOffset|.
|
| + //
|
| + // Note that this can't just find a run whose stringBegin() equals the
|
| + // stringEnd() of the previous run because CoreText on Mac OS X 10.6 does
|
| + // not return runs covering BiDi control chars, so this has to handle the
|
| + // resulting gaps.
|
| + unsigned result = 0;
|
| + unsigned lowestOffset = UINT_MAX;
|
| + for (unsigned i = 0; i < m_complexTextRuns.size(); ++i) {
|
| + unsigned offset = stringBegin(*m_complexTextRuns[i]);
|
| + if (i != runIndex && offset >= runOffset && offset < lowestOffset) {
|
| + lowestOffset = offset;
|
| + result = i;
|
| + }
|
| + }
|
| +
|
| + ASSERT(lowestOffset != UINT_MAX);
|
| + return result;
|
| +}
|
| +
|
| +unsigned ComplexTextController::indexOfCurrentRun(unsigned& leftmostGlyph)
|
| +{
|
| + leftmostGlyph = 0;
|
| +
|
| + size_t runCount = m_complexTextRuns.size();
|
| + if (m_currentRun >= runCount)
|
| + return runCount;
|
| +
|
| + if (m_isLTROnly) {
|
| + for (unsigned i = 0; i < m_currentRun; ++i)
|
| + leftmostGlyph += m_complexTextRuns[i]->glyphCount();
|
| + return m_currentRun;
|
| + }
|
| +
|
| + if (m_runIndices.isEmpty()) {
|
| + unsigned firstRun = 0;
|
| + unsigned firstRunOffset = stringBegin(*m_complexTextRuns[0]);
|
| + for (unsigned i = 1; i < runCount; ++i) {
|
| + unsigned offset = stringBegin(*m_complexTextRuns[i]);
|
| + if (offset < firstRunOffset) {
|
| + firstRun = i;
|
| + firstRunOffset = offset;
|
| + }
|
| + }
|
| + m_runIndices.uncheckedAppend(firstRun);
|
| + }
|
| +
|
| + while (m_runIndices.size() <= m_currentRun) {
|
| + m_runIndices.uncheckedAppend(findNextRunIndex(m_runIndices.last()));
|
| + }
|
| +
|
| + unsigned currentRunIndex = m_runIndices[m_currentRun];
|
| + leftmostGlyph = m_glyphCountFromStartToIndex[currentRunIndex];
|
| + return currentRunIndex;
|
| +}
|
| +
|
| +unsigned ComplexTextController::incrementCurrentRun(unsigned& leftmostGlyph)
|
| +{
|
| + if (m_isLTROnly) {
|
| + leftmostGlyph += m_complexTextRuns[m_currentRun++]->glyphCount();
|
| + return m_currentRun;
|
| + }
|
| +
|
| + m_currentRun++;
|
| + leftmostGlyph = 0;
|
| + return indexOfCurrentRun(leftmostGlyph);
|
| +}
|
| +
|
| +void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer, GlyphIterationStyle iterationStyle, HashSet<const SimpleFontData*>* fallbackFonts)
|
| +{
|
| + if (static_cast<int>(offset) > m_end)
|
| + offset = m_end;
|
| +
|
| + if (offset <= m_currentCharacter) {
|
| + m_runWidthSoFar = m_leadingExpansion;
|
| + m_numGlyphsSoFar = 0;
|
| + m_currentRun = 0;
|
| + m_glyphInCurrentRun = 0;
|
| + m_characterInCurrentGlyph = 0;
|
| + }
|
| +
|
| + m_currentCharacter = offset;
|
| +
|
| + size_t runCount = m_complexTextRuns.size();
|
| +
|
| + unsigned leftmostGlyph = 0;
|
| + unsigned currentRunIndex = indexOfCurrentRun(leftmostGlyph);
|
| + while (m_currentRun < runCount) {
|
| + const ComplexTextRun& complexTextRun = *m_complexTextRuns[currentRunIndex];
|
| + bool ltr = complexTextRun.isLTR();
|
| + size_t glyphCount = complexTextRun.glyphCount();
|
| + unsigned g = ltr ? m_glyphInCurrentRun : glyphCount - 1 - m_glyphInCurrentRun;
|
| + unsigned k = leftmostGlyph + g;
|
| + if (fallbackFonts && complexTextRun.fontData() != m_font.primaryFont())
|
| + fallbackFonts->add(complexTextRun.fontData());
|
| +
|
| + while (m_glyphInCurrentRun < glyphCount) {
|
| + unsigned glyphStartOffset = complexTextRun.indexAt(g);
|
| + unsigned glyphEndOffset;
|
| + if (complexTextRun.isMonotonic()) {
|
| + if (ltr)
|
| + glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd()));
|
| + else
|
| + glyphEndOffset = std::max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd()));
|
| + } else
|
| + glyphEndOffset = complexTextRun.endOffsetAt(g);
|
| +
|
| + CGSize adjustedAdvance = m_adjustedAdvances[k];
|
| +
|
| + if (glyphStartOffset + complexTextRun.stringLocation() >= m_currentCharacter)
|
| + return;
|
| +
|
| + if (glyphBuffer && !m_characterInCurrentGlyph)
|
| + glyphBuffer->add(m_adjustedGlyphs[k], complexTextRun.fontData(), FloatSize(adjustedAdvance));
|
| +
|
| + unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph;
|
| + m_characterInCurrentGlyph = std::min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset;
|
| + // FIXME: Instead of dividing the glyph's advance equally between the characters, this
|
| + // could use the glyph's "ligature carets". However, there is no Core Text API to get the
|
| + // ligature carets.
|
| + if (glyphStartOffset == glyphEndOffset) {
|
| + // When there are multiple glyphs per character we need to advance by the full width of the glyph.
|
| + ASSERT(m_characterInCurrentGlyph == oldCharacterInCurrentGlyph);
|
| + m_runWidthSoFar += adjustedAdvance.width;
|
| + } else if (iterationStyle == ByWholeGlyphs) {
|
| + if (!oldCharacterInCurrentGlyph)
|
| + m_runWidthSoFar += adjustedAdvance.width;
|
| + } else
|
| + m_runWidthSoFar += adjustedAdvance.width * (m_characterInCurrentGlyph - oldCharacterInCurrentGlyph) / (glyphEndOffset - glyphStartOffset);
|
| +
|
| + if (glyphEndOffset + complexTextRun.stringLocation() > m_currentCharacter)
|
| + return;
|
| +
|
| + m_numGlyphsSoFar++;
|
| + m_glyphInCurrentRun++;
|
| + m_characterInCurrentGlyph = 0;
|
| + if (ltr) {
|
| + g++;
|
| + k++;
|
| + } else {
|
| + g--;
|
| + k--;
|
| + }
|
| + }
|
| + currentRunIndex = incrementCurrentRun(leftmostGlyph);
|
| + m_glyphInCurrentRun = 0;
|
| + }
|
| +}
|
| +
|
| +void ComplexTextController::adjustGlyphsAndAdvances()
|
| +{
|
| + CGFloat widthSinceLastCommit = 0;
|
| + size_t runCount = m_complexTextRuns.size();
|
| + bool hasExtraSpacing = (m_font.fontDescription().letterSpacing() || m_font.fontDescription().wordSpacing() || m_expansion) && !m_run.spacingDisabled();
|
| + for (size_t r = 0; r < runCount; ++r) {
|
| + ComplexTextRun& complexTextRun = *m_complexTextRuns[r];
|
| + unsigned glyphCount = complexTextRun.glyphCount();
|
| + const SimpleFontData* fontData = complexTextRun.fontData();
|
| +
|
| + if (!complexTextRun.isLTR())
|
| + m_isLTROnly = false;
|
| +
|
| + const CGGlyph* glyphs = complexTextRun.glyphs();
|
| + const CGSize* advances = complexTextRun.advances();
|
| +
|
| + bool lastRun = r + 1 == runCount;
|
| + bool roundsAdvances = fontData->platformData().roundsGlyphAdvances();
|
| + float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset();
|
| + const UChar* cp = complexTextRun.characters();
|
| + CGPoint glyphOrigin = CGPointZero;
|
| + CFIndex lastCharacterIndex = m_run.ltr() ? std::numeric_limits<CFIndex>::min() : std::numeric_limits<CFIndex>::max();
|
| + bool isMonotonic = true;
|
| +
|
| + for (unsigned i = 0; i < glyphCount; i++) {
|
| + CFIndex characterIndex = complexTextRun.indexAt(i);
|
| + if (m_run.ltr()) {
|
| + if (characterIndex < lastCharacterIndex)
|
| + isMonotonic = false;
|
| + } else {
|
| + if (characterIndex > lastCharacterIndex)
|
| + isMonotonic = false;
|
| + }
|
| + UChar ch = *(cp + characterIndex);
|
| + bool lastGlyph = lastRun && i + 1 == glyphCount;
|
| + UChar nextCh;
|
| + if (lastGlyph)
|
| + nextCh = ' ';
|
| + else if (i + 1 < glyphCount)
|
| + nextCh = *(cp + complexTextRun.indexAt(i + 1));
|
| + else
|
| + nextCh = *(m_complexTextRuns[r + 1]->characters() + m_complexTextRuns[r + 1]->indexAt(0));
|
| +
|
| + bool treatAsSpace = Character::treatAsSpace(ch);
|
| + CGGlyph glyph = treatAsSpace ? fontData->spaceGlyph() : glyphs[i];
|
| + CGSize advance = treatAsSpace ? CGSizeMake(spaceWidth, advances[i].height) : advances[i];
|
| +
|
| + if (ch == '\t' && m_run.allowTabs()) {
|
| + advance.width = m_font.tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_totalWidth + widthSinceLastCommit);
|
| + } else if (Character::treatAsZeroWidthSpace(ch) && !treatAsSpace) {
|
| + advance.width = 0;
|
| + glyph = fontData->spaceGlyph();
|
| + }
|
| +
|
| + float roundedAdvanceWidth = roundf(advance.width);
|
| + if (roundsAdvances)
|
| + advance.width = roundedAdvanceWidth;
|
| +
|
| + advance.width += fontData->syntheticBoldOffset();
|
| +
|
| + if (hasExtraSpacing) {
|
| + // If we're a glyph with an advance, go ahead and add in letter-spacing.
|
| + // That way we weed out zero width lurkers. This behavior matches the fast text code path.
|
| + if (advance.width && m_font.fontDescription().letterSpacing())
|
| + advance.width += m_font.fontDescription().letterSpacing();
|
| +
|
| + // Handle justification and word-spacing.
|
| + if (treatAsSpace || Character::isCJKIdeographOrSymbol(ch)) {
|
| + // Distribute the run's total expansion evenly over all expansion opportunities in the run.
|
| + if (m_expansion) {
|
| + if (!treatAsSpace && !m_afterExpansion) {
|
| + // Take the expansion opportunity before this ideograph.
|
| + m_expansion -= m_expansionPerOpportunity;
|
| + float expansionAtThisOpportunity = m_expansionPerOpportunity;
|
| + m_totalWidth += expansionAtThisOpportunity;
|
| + if (m_adjustedAdvances.isEmpty())
|
| + m_leadingExpansion = expansionAtThisOpportunity;
|
| + else
|
| + m_adjustedAdvances.last().width += expansionAtThisOpportunity;
|
| + }
|
| + if (!lastGlyph || m_run.allowsTrailingExpansion()) {
|
| + m_expansion -= m_expansionPerOpportunity;
|
| + advance.width += m_expansionPerOpportunity;
|
| + m_afterExpansion = true;
|
| + }
|
| + } else
|
| + m_afterExpansion = false;
|
| +
|
| + // Account for word-spacing.
|
| + if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.fontDescription().wordSpacing())
|
| + advance.width += m_font.fontDescription().wordSpacing();
|
| + } else
|
| + m_afterExpansion = false;
|
| + }
|
| +
|
| + widthSinceLastCommit += advance.width;
|
| +
|
| + // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space.
|
| + if (m_forTextEmphasis && (!Character::canReceiveTextEmphasis(ch) || (U_GET_GC_MASK(ch) & U_GC_M_MASK)))
|
| + glyph = 0;
|
| +
|
| + advance.height *= -1;
|
| + m_adjustedAdvances.append(advance);
|
| + m_adjustedGlyphs.append(glyph);
|
| +
|
| + FloatRect glyphBounds = fontData->boundsForGlyph(glyph);
|
| + glyphBounds.move(glyphOrigin.x, glyphOrigin.y);
|
| + m_minGlyphBoundingBoxX = std::min(m_minGlyphBoundingBoxX, glyphBounds.x());
|
| + m_maxGlyphBoundingBoxX = std::max(m_maxGlyphBoundingBoxX, glyphBounds.maxX());
|
| + m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, glyphBounds.y());
|
| + m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, glyphBounds.maxY());
|
| + glyphOrigin.x += advance.width;
|
| + glyphOrigin.y += advance.height;
|
| +
|
| + lastCharacterIndex = characterIndex;
|
| + }
|
| + if (!isMonotonic)
|
| + complexTextRun.setIsNonMonotonic();
|
| + }
|
| + m_totalWidth += widthSinceLastCommit;
|
| +}
|
| +
|
| +} // namespace blink
|
|
|