Skip to content

Commit aff020e

Browse files
committed
feat: cache marker metrics
1 parent 1175c57 commit aff020e

2 files changed

Lines changed: 69 additions & 14 deletions

File tree

android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,8 @@ open class EnrichedOrderedListSpan(
2727
paint: Paint,
2828
highestIndex: Int,
2929
): Boolean {
30-
val highestIndexText = "$highestIndex."
31-
32-
val originalTypeface = paint.typeface
33-
paint.typeface = getTypeface(enrichedStyle.olMarkerFontWeight, originalTypeface)
34-
val highestIndexWidth = ceil(paint.measureText(highestIndexText)).toInt()
35-
paint.typeface = originalTypeface
30+
val (digitWidth, dotWidth) = markerMetricsFor(paint, paint.typeface)
31+
val highestIndexWidth = ceil(digitCountOf(highestIndex) * digitWidth + dotWidth).toInt()
3632

3733
val newColumnMargin = max(enrichedStyle.olMarginLeft, highestIndexWidth)
3834
if (newColumnMargin == columnMargin) return false
@@ -99,4 +95,39 @@ open class EnrichedOrderedListSpan(
9995
Typeface.create(originalTypeface, Typeface.NORMAL)
10096
}
10197
}
98+
99+
private fun markerMetricsFor(
100+
paint: Paint,
101+
baseTypeface: Typeface,
102+
): Pair<Float, Float> {
103+
val key = MarkerMetricsKey(baseTypeface, enrichedStyle.olMarkerFontWeight, paint.textSize)
104+
return markerMetricsCache.getOrPut(key) {
105+
val originalTypeface = paint.typeface
106+
paint.typeface = getTypeface(enrichedStyle.olMarkerFontWeight, baseTypeface)
107+
val digitWidth = paint.measureText("0")
108+
val dotWidth = paint.measureText(".")
109+
paint.typeface = originalTypeface
110+
Pair(digitWidth, dotWidth)
111+
}
112+
}
113+
114+
private data class MarkerMetricsKey(
115+
val baseTypeface: Typeface,
116+
val fontWeight: Int?,
117+
val textSize: Float,
118+
)
119+
120+
companion object {
121+
private val markerMetricsCache = mutableMapOf<MarkerMetricsKey, Pair<Float, Float>>()
122+
123+
private fun digitCountOf(n: Int): Int {
124+
var count = 1
125+
var value = max(n, 1)
126+
while (value >= 10) {
127+
value /= 10
128+
count++
129+
}
130+
return count
131+
}
132+
}
102133
}

ios/styles/OrderedListStyle.mm

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
#import "StyleUtils.h"
55
#import "TextInsertionUtils.h"
66

7-
@implementation OrderedListStyle
7+
@implementation OrderedListStyle {
8+
// we don't want to re-measure each marker's actual
9+
// width. We estimate the width with cached metrics instead
10+
UIFont *_cachedMarkerFont;
11+
CGFloat _cachedDigitWidth;
12+
CGFloat _cachedDotWidth;
13+
}
814

915
+ (StyleType)getType {
1016
return OrderedList;
@@ -87,17 +93,35 @@ - (void)recalculateListsAroundEditedRange:(NSRange)range {
8793
}
8894
}
8995

96+
- (void)ensureMarkerMetricsForFont:(UIFont *)font {
97+
if (_cachedMarkerFont != nil && [_cachedMarkerFont isEqual:font]) {
98+
return;
99+
}
100+
_cachedMarkerFont = font;
101+
NSDictionary *attrs = @{NSFontAttributeName : font};
102+
_cachedDigitWidth = [@"0" sizeWithAttributes:attrs].width;
103+
_cachedDotWidth = [@"." sizeWithAttributes:attrs].width;
104+
}
105+
106+
- (NSInteger)digitCountOf:(NSInteger)n {
107+
NSInteger count = 1;
108+
NSInteger value = MAX(n, 1);
109+
while (value >= 10) {
110+
value /= 10;
111+
count += 1;
112+
}
113+
return count;
114+
}
115+
90116
// computes the shared marker-column indent for a list of the given item
91117
// count. The largest marker value equals the item count (numbering starts
92118
// at 1); if its width overflows the configured margin we expand to fit it
93119
- (CGFloat)headIndentForItemCount:(NSInteger)itemCount {
94-
NSString *widestMarker =
95-
[NSString stringWithFormat:@"%d.", (int)MAX(itemCount, 1)];
96-
CGFloat widestMarkerWidth =
97-
[widestMarker sizeWithAttributes:@{
98-
NSFontAttributeName : [self.host.config orderedListMarkerFont]
99-
}]
100-
.width;
120+
UIFont *markerFont = [self.host.config orderedListMarkerFont];
121+
[self ensureMarkerMetricsForFont:markerFont];
122+
123+
NSInteger digitCount = [self digitCountOf:itemCount];
124+
CGFloat widestMarkerWidth = digitCount * _cachedDigitWidth + _cachedDotWidth;
101125

102126
CGFloat markerColumnWidth =
103127
MAX([self.host.config orderedListMarginLeft], widestMarkerWidth);

0 commit comments

Comments
 (0)