static unsigned rightTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer) { ASSERT(keepCount < length); ASSERT(keepCount < STRING_BUFFER_SIZE); TextBreakIterator* it = characterBreakIterator(string.characters(), length); unsigned keepLength = textBreakAtOrPreceding(it, keepCount); unsigned truncatedLength = keepLength + 1; memcpy(buffer, string.characters(), sizeof(UChar) * keepLength); buffer[keepLength] = horizontalEllipsis; return truncatedLength; }
static unsigned centerTruncateToBuffer(const String& string, unsigned length, unsigned keepCount, UChar* buffer) { ASSERT(keepCount < length); ASSERT(keepCount < STRING_BUFFER_SIZE); unsigned omitStart = (keepCount + 1) / 2; TextBreakIterator* it = characterBreakIterator(string.characters(), length); unsigned omitEnd = boundedTextBreakFollowing(it, omitStart + (length - keepCount) - 1, length); omitStart = textBreakAtOrPreceding(it, omitStart); unsigned truncatedLength = omitStart + 1 + (length - omitEnd); ASSERT(truncatedLength <= length); memcpy(buffer, string.characters(), sizeof(UChar) * omitStart); buffer[omitStart] = horizontalEllipsis; memcpy(&buffer[omitStart + 1], &string.characters()[omitEnd], sizeof(UChar) * (length - omitEnd)); return truncatedLength; }
PassRefPtr<Text> Text::createWithLengthLimit(Document* doc, const String& text, unsigned& charsLeft, unsigned maxChars) { if (charsLeft == text.length() && charsLeft <= maxChars) { charsLeft = 0; return new Text(doc, text); } unsigned start = text.length() - charsLeft; unsigned end = start + std::min(charsLeft, maxChars); // check we are not on an unbreakable boundary TextBreakIterator* it = characterBreakIterator(text.characters(), text.length()); if (end < text.length() && !isTextBreak(it, end)) end = textBreakPreceding(it, end); // maxChars of unbreakable characters could lead to infinite loop if (end <= start) end = text.length(); String nodeText = text.substring(start, end - start); charsLeft = text.length() - end; return new Text(doc, nodeText); }
TextBreakIterator* cursorMovementIterator(const UChar* string, int length) { // FIXME: This needs closer inspection to achieve behaviour identical to the ICU version. return characterBreakIterator(string, length); }
TextBreakIterator* cursorMovementIterator(const UChar* string, int length) { return characterBreakIterator(string, length); }
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; }