Skip to content

Commit efdcce7

Browse files
authored
Merge pull request #146 from DemchaAV/perf/autosize-binary-search
perf(engine): binary-search auto-size font fitting + deprecate unused adjustFontSizeToFit (F12)
2 parents 16b40b9 + 256e84d commit efdcce7

5 files changed

Lines changed: 72 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ Open cycle — bug-fix / housekeeping. Entries land here as they merge.
8989
the per-measurement path). **Measured line metrics are unchanged.** No public API
9090
or behaviour change.
9191

92+
- **Auto-size font fitting binary-searches the size grid.** A paragraph with
93+
`autoSize(...)` resolved its font size by scanning every step from max down to
94+
min, re-measuring the line at each candidate (up to ~50 measurements). Line width
95+
is linear in font size, so the fit is monotonic — the search now binary-searches
96+
the grid for the same boundary in ~log2(n) measurements instead of n. **Output is
97+
byte-identical** — it returns the same grid size the linear scan did (covered by
98+
the existing auto-size integration and snapshot tests). No public API or behaviour
99+
change.
100+
101+
### Deprecations
102+
103+
- **`Font.adjustFontSizeToFit(...)` is deprecated.** The engine-internal
104+
`Font#adjustFontSizeToFit` (and its `PdfFont` / `WordFont` implementations) is
105+
unused and incorrect — the only real implementation re-measured with the
106+
unchanged style, so it always returned the minimum size. Canonical auto-size is
107+
resolved by the layout compiler. The method is kept for binary compatibility and
108+
scheduled for removal in the next major.
109+
92110
### Tests / tooling
93111

94112
- **Benchmark regression gate and measurement probe (benchmarks module, not part

src/main/java/com/demcha/compose/document/layout/TextFlowSupport.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -585,15 +585,30 @@ private static DocumentTextStyle resolveAutoSizeTextStyle(ParagraphNode node,
585585
double minSize = autoSize.minSize();
586586
double step = Math.max(0.1, autoSize.step());
587587

588-
// Single-line text: shrink the font size until the longest logical line
589-
// measures inside the available inner width, otherwise fall back to the
590-
// smallest configured size.
591-
for (double size = maxSize; size >= minSize - 1e-6; size -= step) {
592-
DocumentTextStyle candidate = baseStyle.withSize(size);
588+
// Single-line text: pick the largest grid size (maxSize, maxSize-step, …,
589+
// down to >= minSize) whose longest logical line measures inside the
590+
// available inner width, otherwise fall back to the smallest configured
591+
// size. The fit predicate is monotonic in size (line width is linear in
592+
// size), so binary-search the grid for the boundary instead of measuring
593+
// at every step — the same size the linear scan returned, in ~log2(n)
594+
// measurements rather than n.
595+
int maxStepCount = (int) Math.floor((maxSize - minSize + 1e-6) / step);
596+
int lo = 0;
597+
int hi = maxStepCount;
598+
int fitStep = -1;
599+
while (lo <= hi) {
600+
int mid = (lo + hi) >>> 1;
601+
DocumentTextStyle candidate = baseStyle.withSize(maxSize - mid * step);
593602
if (paragraphFitsSingleLine(node, candidate, innerWidth, measurement)) {
594-
return candidate;
603+
fitStep = mid; // fits — try a larger size (fewer steps down)
604+
hi = mid - 1;
605+
} else {
606+
lo = mid + 1; // too wide — need a smaller size (more steps)
595607
}
596608
}
609+
if (fitStep >= 0) {
610+
return baseStyle.withSize(maxSize - fitStep * step);
611+
}
597612
return baseStyle.withSize(minSize);
598613
}
599614

src/main/java/com/demcha/compose/engine/font/Font.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,20 @@ default T fontType(TextDecoration textDecoration) {
4646

4747
double scale(double size);
4848

49-
public TextStyle adjustFontSizeToFit(String text, TextStyle style, double availableWidth);
49+
/**
50+
* @param text the text to fit
51+
* @param style the starting style
52+
* @param availableWidth the width to fit within
53+
* @return a re-sized style
54+
* @deprecated Unused and incorrect: the only real implementation re-measures
55+
* with the unchanged {@code style}, so the loop never converges and the
56+
* result is always the minimum size. Canonical auto-size is resolved by the
57+
* layout compiler ({@code TextFlowSupport.resolveAutoSizeTextStyle}); this
58+
* method has no callers and is kept only for binary compatibility,
59+
* scheduled for removal in the next major.
60+
*/
61+
@Deprecated
62+
TextStyle adjustFontSizeToFit(String text, TextStyle style, double availableWidth);
5063

5164
ContentSize getTightBounds(String text, TextStyle style);
5265
}

src/main/java/com/demcha/compose/engine/render/pdf/PdfFont.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,18 @@ public PdfFont(PDFont defaultFont, PDFont bold, PDFont italic, PDFont boldItalic
2929
}
3030

3131

32-
// Adjust font size automatically based on the width of the text and available space
32+
/**
33+
* @param text the text to fit
34+
* @param style the starting style
35+
* @param availableWidth the width to fit within
36+
* @return a re-sized style
37+
* @deprecated Unused and incorrect — it re-measures with the unchanged
38+
* {@code style}, so {@code textWidth} never shrinks and the loop runs to the
39+
* minimum size regardless of fit. Canonical auto-size is handled by the
40+
* layout compiler; kept only for binary compatibility.
41+
*/
42+
@Deprecated
43+
@Override
3344
public TextStyle adjustFontSizeToFit(String text, TextStyle style, double availableWidth) {
3445
double textWidth = getTextWidth(style, text);
3546

@@ -39,7 +50,6 @@ public TextStyle adjustFontSizeToFit(String text, TextStyle style, double availa
3950
newSize--; // Reduce size
4051
textWidth = getTextWidth(style, text); // Recalculate text width
4152
}
42-
PDFont pdFont = fontType(style.decoration());
4353
return new TextStyle(style.fontName(), newSize, style.decoration(), style.color());
4454
}
4555

src/main/java/com/demcha/compose/engine/render/word/WordFont.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,14 @@ public double scale(double size) {
8383
}
8484

8585
/**
86-
* @param text
87-
* @param style
88-
* @param availableWidth
89-
* @return
86+
* @param text the text to fit
87+
* @param style the starting style
88+
* @param availableWidth the width to fit within
89+
* @return {@code null} — never implemented
90+
* @deprecated Unused; never implemented (returns {@code null}). Kept only for
91+
* binary compatibility. See {@code Font#adjustFontSizeToFit}.
9092
*/
93+
@Deprecated
9194
@Override
9295
public TextStyle adjustFontSizeToFit(String text, TextStyle style, double availableWidth) {
9396
return null;

0 commit comments

Comments
 (0)