Index: Source/core/platform/graphics/mac/ComplexTextController.cpp |
diff --git a/Source/core/platform/graphics/mac/ComplexTextController.cpp b/Source/core/platform/graphics/mac/ComplexTextController.cpp |
deleted file mode 100644 |
index 9e34f8a5d853411e3ca58adf2f58149a9f091817..0000000000000000000000000000000000000000 |
--- a/Source/core/platform/graphics/mac/ComplexTextController.cpp |
+++ /dev/null |
@@ -1,727 +0,0 @@ |
-/* |
- * 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 "core/platform/graphics/mac/ComplexTextController.h" |
- |
-#include <ApplicationServices/ApplicationServices.h> |
-#include "core/platform/graphics/Font.h" |
-#include "platform/geometry/FloatSize.h" |
-#include "platform/graphics/TextRun.h" |
-#include "platform/text/TextBreakIterator.h" |
-#include "wtf/StdLibExtras.h" |
-#include "wtf/unicode/CharacterNames.h" |
- |
-using namespace std; |
- |
-namespace WebCore { |
- |
-class TextLayout { |
-public: |
- static bool isNeeded(const TextRun& run, const Font& font) |
- { |
- return font.codePath(run) == Font::Complex; |
- } |
- |
- TextLayout(const TextRun& run, unsigned textLength, const Font& font, float xPos) |
- : m_font(font) |
- , m_run(constructTextRun(run, textLength, font, xPos)) |
- , m_controller(adoptPtr(new ComplexTextController(&m_font, m_run, true))) |
- { |
- } |
- |
- float width(unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts) |
- { |
- m_controller->advance(from, 0, ByWholeGlyphs, fallbackFonts); |
- float beforeWidth = m_controller->runWidthSoFar(); |
- if (m_font.wordSpacing() && from && Font::treatAsSpace(m_run[from])) |
- beforeWidth += m_font.wordSpacing(); |
- m_controller->advance(from + len, 0, ByWholeGlyphs, fallbackFonts); |
- float afterWidth = m_controller->runWidthSoFar(); |
- return afterWidth - beforeWidth; |
- } |
- |
-private: |
- static TextRun constructTextRun(const TextRun& textRun, unsigned textLength, const Font& font, float xPos) |
- { |
- TextRun run = textRun; |
- run.setCharactersLength(textLength); |
- ASSERT(run.charactersLength() >= run.length()); |
- |
- run.setXPos(xPos); |
- return run; |
- } |
- |
- // ComplexTextController has only references to its Font and TextRun so they must be kept alive here. |
- Font m_font; |
- TextRun m_run; |
- OwnPtr<ComplexTextController> m_controller; |
-}; |
- |
-PassOwnPtr<TextLayout> Font::createLayoutForMacComplexText(const TextRun& run, unsigned textLength, float xPos, bool collapseWhiteSpace) const |
-{ |
- if (!collapseWhiteSpace || !TextLayout::isNeeded(run, *this)) |
- return nullptr; |
- return adoptPtr(new TextLayout(run, textLength, *this, xPos)); |
-} |
- |
-void Font::deleteLayout(TextLayout* layout) |
-{ |
- delete layout; |
-} |
- |
-float Font::width(TextLayout& layout, unsigned from, unsigned len, HashSet<const SimpleFontData*>* fallbackFonts) |
-{ |
- return layout.width(from, len, fallbackFonts); |
-} |
- |
-static inline CGFloat roundCGFloat(CGFloat f) |
-{ |
- if (sizeof(CGFloat) == sizeof(float)) |
- return roundf(static_cast<float>(f)); |
- return static_cast<CGFloat>(round(f)); |
-} |
- |
-static inline CGFloat ceilCGFloat(CGFloat f) |
-{ |
- if (sizeof(CGFloat) == sizeof(float)) |
- return ceilf(static_cast<float>(f)); |
- return static_cast<CGFloat>(ceil(f)); |
-} |
- |
-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_finalRoundingWidth(0) |
- , m_expansion(run.expansion()) |
- , m_leadingExpansion(0) |
- , m_afterExpansion(!run.allowsLeadingExpansion()) |
- , m_fallbackFonts(fallbackFonts) |
- , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) |
- , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) |
- , m_minGlyphBoundingBoxY(numeric_limits<float>::max()) |
- , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) |
- , m_lastRoundingGlyph(0) |
-{ |
- if (!m_expansion) |
- m_expansionPerOpportunity = 0; |
- else { |
- bool isAfterExpansion = m_afterExpansion; |
- unsigned expansionOpportunityCount; |
- if (m_run.is8Bit()) |
- expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters8(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); |
- else |
- expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters16(), m_end, m_run.ltr() ? LTR : RTL, 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 = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); |
- else |
- hitGlyphEnd = 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.isSmallCaps()) |
- 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.isSmallCaps() && !(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.isSmallCaps()) { |
- 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 = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd())); |
- else |
- glyphEndOffset = 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(), adjustedAdvance); |
- |
- unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; |
- m_characterInCurrentGlyph = 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; |
- } |
- if (!m_run.ltr() && m_numGlyphsSoFar == m_adjustedAdvances.size()) |
- m_runWidthSoFar += m_finalRoundingWidth; |
-} |
- |
-void ComplexTextController::adjustGlyphsAndAdvances() |
-{ |
- CGFloat widthSinceLastCommit = 0; |
- size_t runCount = m_complexTextRuns.size(); |
- bool hasExtraSpacing = (m_font.letterSpacing() || m_font.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 = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); |
- float spaceWidth = fontData->spaceWidth() - fontData->syntheticBoldOffset(); |
- CGFloat roundedSpaceWidth = roundCGFloat(spaceWidth); |
- const UChar* cp = complexTextRun.characters(); |
- CGPoint glyphOrigin = CGPointZero; |
- CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : 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 = Font::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 (Font::treatAsZeroWidthSpace(ch) && !treatAsSpace) { |
- advance.width = 0; |
- glyph = fontData->spaceGlyph(); |
- } |
- |
- float roundedAdvanceWidth = roundf(advance.width); |
- if (roundsAdvances) |
- advance.width = roundedAdvanceWidth; |
- |
- advance.width += fontData->syntheticBoldOffset(); |
- |
- |
- // We special case spaces in two ways when applying word rounding. |
- // First, we round spaces to an adjusted width in all fonts. |
- // Second, in fixed-pitch fonts we ensure that all glyphs that |
- // match the width of the space glyph have the same width as the space glyph. |
- if (m_run.applyWordRounding() && roundedAdvanceWidth == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) |
- advance.width = fontData->adjustedSpaceWidth(); |
- |
- 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.letterSpacing()) |
- advance.width += m_font.letterSpacing(); |
- |
- // Handle justification and word-spacing. |
- if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) { |
- // Distribute the run's total expansion evenly over all expansion opportunities in the run. |
- if (m_expansion) { |
- float previousExpansion = m_expansion; |
- if (!treatAsSpace && !m_afterExpansion) { |
- // Take the expansion opportunity before this ideograph. |
- m_expansion -= m_expansionPerOpportunity; |
- float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); |
- m_totalWidth += expansionAtThisOpportunity; |
- if (m_adjustedAdvances.isEmpty()) |
- m_leadingExpansion = expansionAtThisOpportunity; |
- else |
- m_adjustedAdvances.last().width += expansionAtThisOpportunity; |
- previousExpansion = m_expansion; |
- } |
- if (!lastGlyph || m_run.allowsTrailingExpansion()) { |
- m_expansion -= m_expansionPerOpportunity; |
- advance.width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); |
- m_afterExpansion = true; |
- } |
- } else |
- m_afterExpansion = false; |
- |
- // Account for word-spacing. |
- if (treatAsSpace && (ch != '\t' || !m_run.allowTabs()) && (characterIndex > 0 || r > 0) && m_font.wordSpacing()) |
- advance.width += m_font.wordSpacing(); |
- } else |
- m_afterExpansion = false; |
- } |
- |
- // Apply rounding hacks if needed. |
- // We adjust the width of the last character of a "word" to ensure an integer width. |
- // Force characters that are used to determine word boundaries for the rounding hack |
- // to be integer width, so the following words will start on an integer boundary. |
- if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(ch)) |
- advance.width = ceilCGFloat(advance.width); |
- |
- // Check to see if the next character is a "rounding hack character", if so, adjust the |
- // width so that the total run width will be on an integer boundary. |
- if ((m_run.applyWordRounding() && !lastGlyph && Font::isRoundingHackCharacter(nextCh)) || (m_run.applyRunRounding() && lastGlyph)) { |
- CGFloat totalWidth = widthSinceLastCommit + advance.width; |
- widthSinceLastCommit = ceilCGFloat(totalWidth); |
- CGFloat extraWidth = widthSinceLastCommit - totalWidth; |
- if (m_run.ltr()) |
- advance.width += extraWidth; |
- else { |
- if (m_lastRoundingGlyph) |
- m_adjustedAdvances[m_lastRoundingGlyph - 1].width += extraWidth; |
- else |
- m_finalRoundingWidth = extraWidth; |
- m_lastRoundingGlyph = m_adjustedAdvances.size() + 1; |
- } |
- m_totalWidth += widthSinceLastCommit; |
- widthSinceLastCommit = 0; |
- } else |
- widthSinceLastCommit += advance.width; |
- |
- // FIXME: Combining marks should receive a text emphasis mark if they are combine with a space. |
- if (m_forTextEmphasis && (!Font::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 = min(m_minGlyphBoundingBoxX, glyphBounds.x()); |
- m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); |
- m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); |
- m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); |
- glyphOrigin.x += advance.width; |
- glyphOrigin.y += advance.height; |
- |
- lastCharacterIndex = characterIndex; |
- } |
- if (!isMonotonic) |
- complexTextRun.setIsNonMonotonic(); |
- } |
- m_totalWidth += widthSinceLastCommit; |
-} |
- |
-} // namespace WebCore |