static String truncateString(const String& string, float maxWidth, const Font& font, TruncationFunction truncateToBuffer, bool disableRoundingHacks) { if (string.isEmpty()) return string; ASSERT(maxWidth >= 0); float currentEllipsisWidth = stringWidth(font, &horizontalEllipsis, 1, disableRoundingHacks); UChar stringBuffer[STRING_BUFFER_SIZE]; unsigned truncatedLength; unsigned keepCount; unsigned length = string.length(); if (length > STRING_BUFFER_SIZE) { keepCount = STRING_BUFFER_SIZE - 1; // need 1 character for the ellipsis truncatedLength = centerTruncateToBuffer(string, length, keepCount, stringBuffer); } else { keepCount = length; memcpy(stringBuffer, string.characters(), sizeof(UChar) * length); truncatedLength = length; } float width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); if (width <= maxWidth) return string; unsigned keepCountForLargestKnownToFit = 0; float widthForLargestKnownToFit = currentEllipsisWidth; unsigned keepCountForSmallestKnownToNotFit = keepCount; float widthForSmallestKnownToNotFit = width; if (currentEllipsisWidth >= maxWidth) { keepCountForLargestKnownToFit = 1; keepCountForSmallestKnownToNotFit = 2; } while (keepCountForLargestKnownToFit + 1 < keepCountForSmallestKnownToNotFit) { ASSERT(widthForLargestKnownToFit <= maxWidth); ASSERT(widthForSmallestKnownToNotFit > maxWidth); float ratio = (keepCountForSmallestKnownToNotFit - keepCountForLargestKnownToFit) / (widthForSmallestKnownToNotFit - widthForLargestKnownToFit); keepCount = static_cast<unsigned>(maxWidth * ratio); if (keepCount <= keepCountForLargestKnownToFit) { keepCount = keepCountForLargestKnownToFit + 1; } else if (keepCount >= keepCountForSmallestKnownToNotFit) { keepCount = keepCountForSmallestKnownToNotFit - 1; } ASSERT(keepCount < length); ASSERT(keepCount > 0); ASSERT(keepCount < keepCountForSmallestKnownToNotFit); ASSERT(keepCount > keepCountForLargestKnownToFit); truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer); width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); if (width <= maxWidth) { keepCountForLargestKnownToFit = keepCount; widthForLargestKnownToFit = width; } else { keepCountForSmallestKnownToNotFit = keepCount; widthForSmallestKnownToNotFit = width; } } if (keepCountForLargestKnownToFit == 0) { keepCountForLargestKnownToFit = 1; } if (keepCount != keepCountForLargestKnownToFit) { keepCount = keepCountForLargestKnownToFit; truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer); } return String(stringBuffer, truncatedLength); }
static String truncateString(const String& string, float maxWidth, const FontCascade& font, TruncationFunction truncateToBuffer, bool disableRoundingHacks, float* resultWidth = nullptr, bool shouldInsertEllipsis = true, float customTruncationElementWidth = 0, bool alwaysTruncate = false) { if (string.isEmpty()) return string; if (resultWidth) *resultWidth = 0; ASSERT(maxWidth >= 0); float currentEllipsisWidth = shouldInsertEllipsis ? stringWidth(font, &horizontalEllipsis, 1, disableRoundingHacks) : customTruncationElementWidth; UChar stringBuffer[STRING_BUFFER_SIZE]; unsigned truncatedLength; unsigned keepCount; unsigned length = string.length(); if (length > STRING_BUFFER_SIZE) { if (shouldInsertEllipsis) keepCount = STRING_BUFFER_SIZE - 1; // need 1 character for the ellipsis else keepCount = 0; truncatedLength = centerTruncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis); } else { keepCount = length; StringView(string).getCharactersWithUpconvert(stringBuffer); truncatedLength = length; } float width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); if (!shouldInsertEllipsis && alwaysTruncate) width += customTruncationElementWidth; if ((width - maxWidth) < 0.0001) { // Ignore rounding errors. if (resultWidth) *resultWidth = width; return string; } unsigned keepCountForLargestKnownToFit = 0; float widthForLargestKnownToFit = currentEllipsisWidth; unsigned keepCountForSmallestKnownToNotFit = keepCount; float widthForSmallestKnownToNotFit = width; if (currentEllipsisWidth >= maxWidth) { keepCountForLargestKnownToFit = 1; keepCountForSmallestKnownToNotFit = 2; } while (keepCountForLargestKnownToFit + 1 < keepCountForSmallestKnownToNotFit) { ASSERT_WITH_SECURITY_IMPLICATION(widthForLargestKnownToFit <= maxWidth); ASSERT_WITH_SECURITY_IMPLICATION(widthForSmallestKnownToNotFit > maxWidth); float ratio = (keepCountForSmallestKnownToNotFit - keepCountForLargestKnownToFit) / (widthForSmallestKnownToNotFit - widthForLargestKnownToFit); keepCount = static_cast<unsigned>(maxWidth * ratio); if (keepCount <= keepCountForLargestKnownToFit) keepCount = keepCountForLargestKnownToFit + 1; else if (keepCount >= keepCountForSmallestKnownToNotFit) keepCount = keepCountForSmallestKnownToNotFit - 1; ASSERT_WITH_SECURITY_IMPLICATION(keepCount < length); ASSERT(keepCount > 0); ASSERT_WITH_SECURITY_IMPLICATION(keepCount < keepCountForSmallestKnownToNotFit); ASSERT_WITH_SECURITY_IMPLICATION(keepCount > keepCountForLargestKnownToFit); truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis); width = stringWidth(font, stringBuffer, truncatedLength, disableRoundingHacks); if (!shouldInsertEllipsis) width += customTruncationElementWidth; if (width <= maxWidth) { keepCountForLargestKnownToFit = keepCount; widthForLargestKnownToFit = width; if (resultWidth) *resultWidth = width; } else { keepCountForSmallestKnownToNotFit = keepCount; widthForSmallestKnownToNotFit = width; } } if (keepCountForLargestKnownToFit == 0) { keepCountForLargestKnownToFit = 1; } if (keepCount != keepCountForLargestKnownToFit) { keepCount = keepCountForLargestKnownToFit; truncatedLength = truncateToBuffer(string, length, keepCount, stringBuffer, shouldInsertEllipsis); } return String(stringBuffer, truncatedLength); }