📄 coretextcontroller.cpp
字号:
/* * Copyright (C) 2007, 2008 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. ``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 * 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 "CoreTextController.h"#if USE(CORE_TEXT)#include "CharacterNames.h"#include "Font.h"#include "FontCache.h"#include "SimpleFontData.h"#include "TextBreakIterator.h"#include <wtf/MathExtras.h>using namespace std;namespace WebCore {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));}CoreTextController::CoreTextRun::CoreTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) : m_CTRun(ctRun) , m_fontData(fontData) , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength){ m_glyphCount = CTRunGetGlyphCount(ctRun); m_indices = CTRunGetStringIndicesPtr(ctRun); if (!m_indices) { m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); CFDataIncreaseLength(m_indicesData.get(), m_glyphCount * sizeof(CFIndex)); m_indices = reinterpret_cast<const CFIndex*>(CFDataGetMutableBytePtr(m_indicesData.get())); CTRunGetStringIndices(ctRun, CFRangeMake(0, 0), const_cast<CFIndex*>(m_indices)); }}// Missing glyphs run constructor. Core Text will not generate a run of missing glyphs, instead falling back on// glyphs from LastResort. We want to use the primary font's missing glyph in order to match the fast text code path.CoreTextController::CoreTextRun::CoreTextRun(const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, bool ltr) : m_fontData(fontData) , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength){ Vector<CFIndex, 16> indices; unsigned r = 0; while (r < stringLength) { indices.append(r); if (U_IS_SURROGATE(characters[r])) { ASSERT(r + 1 < stringLength); ASSERT(U_IS_SURROGATE_LEAD(characters[r])); ASSERT(U_IS_TRAIL(characters[r + 1])); r += 2; } else r++; } m_glyphCount = indices.size(); if (!ltr) { for (unsigned r = 0, end = m_glyphCount - 1; r < m_glyphCount / 2; ++r, --end) std::swap(indices[r], indices[end]); } m_indicesData.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, m_glyphCount * sizeof(CFIndex))); CFDataAppendBytes(m_indicesData.get(), reinterpret_cast<const UInt8*>(indices.data()), m_glyphCount * sizeof(CFIndex)); m_indices = reinterpret_cast<const CFIndex*>(CFDataGetBytePtr(m_indicesData.get()));}CoreTextController::CoreTextController(const Font* font, const TextRun& run, bool mayUseNaturalWritingDirection) : m_font(*font) , m_run(run) , m_mayUseNaturalWritingDirection(mayUseNaturalWritingDirection) , m_currentCharacter(0) , m_end(run.length()) , m_totalWidth(0) , m_runWidthSoFar(0) , m_numGlyphsSoFar(0) , m_currentRun(0) , m_glyphInCurrentRun(0) , m_finalRoundingWidth(0) , m_lastRoundingGlyph(0){ m_padding = m_run.padding(); if (!m_padding) m_padPerSpace = 0; else { float numSpaces = 0; for (int s = 0; s < m_run.length(); s++) if (Font::treatAsSpace(m_run[s])) numSpaces++; if (numSpaces == 0) m_padPerSpace = 0; else m_padPerSpace = ceilf(m_run.padding() / numSpaces); } collectCoreTextRuns(); adjustGlyphsAndAdvances();}int CoreTextController::offsetForPosition(int h, bool includePartialGlyphs){ // FIXME: For positions occurring within a ligature, we should return the closest "ligature caret" or // approximate it by dividing the width of the ligature by the number of characters it encompasses. // However, Core Text does not expose a low-level API for directly finding // out how many characters a ligature encompasses (the "attachment count"). if (h >= m_totalWidth) return m_run.ltr() ? m_end : 0; if (h < 0) return m_run.ltr() ? 0 : m_end; CGFloat x = h; size_t runCount = m_coreTextRuns.size(); size_t offsetIntoAdjustedGlyphs = 0; for (size_t r = 0; r < runCount; ++r) { const CoreTextRun& coreTextRun = m_coreTextRuns[r]; for (unsigned j = 0; j < coreTextRun.glyphCount(); ++j) { CGFloat adjustedAdvance = m_adjustedAdvances[offsetIntoAdjustedGlyphs + j].width; if (x <= adjustedAdvance) { CFIndex hitIndex = coreTextRun.indexAt(j); int stringLength = coreTextRun.stringLength(); TextBreakIterator* characterIterator = characterBreakIterator(coreTextRun.characters(), stringLength); int clusterStart; if (isTextBreak(characterIterator, hitIndex)) clusterStart = hitIndex; else { clusterStart = textBreakPreceding(characterIterator, hitIndex); if (clusterStart == TextBreakDone) clusterStart = 0; } if (!includePartialGlyphs) return coreTextRun.stringLocation() + clusterStart; int clusterEnd = textBreakFollowing(characterIterator, hitIndex); if (clusterEnd == TextBreakDone) clusterEnd = stringLength; CGFloat clusterWidth = adjustedAdvance; // FIXME: The search stops at the boundaries of coreTextRun. In theory, it should go on into neighboring CoreTextRuns // 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 on font fallback should occur within a CTLine. if (clusterEnd - clusterStart > 1) { int firstGlyphBeforeCluster = j - 1; while (firstGlyphBeforeCluster && coreTextRun.indexAt(firstGlyphBeforeCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphBeforeCluster) < clusterEnd) { CGFloat width = m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphBeforeCluster].width; clusterWidth += width; x += width; firstGlyphBeforeCluster--; } unsigned firstGlyphAfterCluster = j + 1; while (firstGlyphAfterCluster < coreTextRun.glyphCount() && coreTextRun.indexAt(firstGlyphAfterCluster) >= clusterStart && coreTextRun.indexAt(firstGlyphAfterCluster) < clusterEnd) { clusterWidth += m_adjustedAdvances[offsetIntoAdjustedGlyphs + firstGlyphAfterCluster].width; firstGlyphAfterCluster++; } } if (x <= clusterWidth / 2) return coreTextRun.stringLocation() + (m_run.ltr() ? clusterStart : clusterEnd); else return coreTextRun.stringLocation() + (m_run.ltr() ? clusterEnd : clusterStart); } x -= adjustedAdvance; } offsetIntoAdjustedGlyphs += coreTextRun.glyphCount(); } ASSERT_NOT_REACHED(); return 0;}void CoreTextController::collectCoreTextRuns(){ if (!m_end) return; // We break up glyph run generation for the string by FontData and (if needed) the use of small caps. const UChar* cp = m_run.characters(); bool hasTrailingSoftHyphen = m_run[m_end - 1] == softHyphen; if (m_font.isSmallCaps() || hasTrailingSoftHyphen) m_smallCapsBuffer.resize(m_end); unsigned indexOfFontTransition = m_run.rtl() ? m_end - 1 : 0; const UChar* curr = m_run.rtl() ? cp + m_end - 1 : cp; const UChar* end = m_run.rtl() ? cp - 1 : cp + m_end; // FIXME: Using HYPHEN-MINUS rather than HYPHEN because Times has a HYPHEN-MINUS glyph that looks like its // SOFT-HYPHEN glyph, and has no HYPHEN glyph. static const UChar hyphen = '-'; if (hasTrailingSoftHyphen && m_run.rtl()) { collectCoreTextRunsForCharacters(&hyphen, 1, m_end - 1, m_font.glyphDataForCharacter(hyphen, false).fontData); indexOfFontTransition--; curr--; } GlyphData glyphData; GlyphData nextGlyphData; bool isSurrogate = U16_IS_SURROGATE(*curr); if (isSurrogate) { if (m_run.ltr()) { if (!U16_IS_SURROGATE_LEAD(curr[0]) || curr + 1 == end || !U16_IS_TRAIL(curr[1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[0], curr[1]), false); } else { if (!U16_IS_TRAIL(curr[0]) || curr -1 == end || !U16_IS_SURROGATE_LEAD(curr[-1])) return; nextGlyphData = m_font.glyphDataForCharacter(U16_GET_SUPPLEMENTARY(curr[-1], curr[0]), false); } } else nextGlyphData = m_font.glyphDataForCharacter(*curr, false); UChar newC = 0; bool isSmallCaps; bool nextIsSmallCaps = !isSurrogate && m_font.isSmallCaps() && !(U_GET_GC_MASK(*curr) & U_GC_M_MASK) && (newC = u_toupper(*curr)) != *curr; if (nextIsSmallCaps) m_smallCapsBuffer[curr - cp] = newC; while (true) { curr = m_run.rtl() ? curr - (isSurrogate ? 2 : 1) : curr + (isSurrogate ? 2 : 1); if (curr == end) break; glyphData = nextGlyphData; isSmallCaps = nextIsSmallCaps; int index = curr - cp; isSurrogate = U16_IS_SURROGATE(*curr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -