Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ Open cycle — bug-fix / housekeeping. Entries land here as they merge.
the per-measurement path). **Measured line metrics are unchanged.** No public API
or behaviour change.

- **Auto-size font fitting binary-searches the size grid.** A paragraph with
`autoSize(...)` resolved its font size by scanning every step from max down to
min, re-measuring the line at each candidate (up to ~50 measurements). Line width
is linear in font size, so the fit is monotonic — the search now binary-searches
the grid for the same boundary in ~log2(n) measurements instead of n. **Output is
byte-identical** — it returns the same grid size the linear scan did (covered by
the existing auto-size integration and snapshot tests). No public API or behaviour
change.

### Deprecations

- **`Font.adjustFontSizeToFit(...)` is deprecated.** The engine-internal
`Font#adjustFontSizeToFit` (and its `PdfFont` / `WordFont` implementations) is
unused and incorrect — the only real implementation re-measured with the
unchanged style, so it always returned the minimum size. Canonical auto-size is
resolved by the layout compiler. The method is kept for binary compatibility and
scheduled for removal in the next major.

### Tests / tooling

- **Benchmark regression gate and measurement probe (benchmarks module, not part
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,15 +585,30 @@ private static DocumentTextStyle resolveAutoSizeTextStyle(ParagraphNode node,
double minSize = autoSize.minSize();
double step = Math.max(0.1, autoSize.step());

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

Expand Down
15 changes: 14 additions & 1 deletion src/main/java/com/demcha/compose/engine/font/Font.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,20 @@ default T fontType(TextDecoration textDecoration) {

double scale(double size);

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

ContentSize getTightBounds(String text, TextStyle style);
}
14 changes: 12 additions & 2 deletions src/main/java/com/demcha/compose/engine/render/pdf/PdfFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,18 @@ public PdfFont(PDFont defaultFont, PDFont bold, PDFont italic, PDFont boldItalic
}


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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,14 @@ public double scale(double size) {
}

/**
* @param text
* @param style
* @param availableWidth
* @return
* @param text the text to fit
* @param style the starting style
* @param availableWidth the width to fit within
* @return {@code null} — never implemented
* @deprecated Unused; never implemented (returns {@code null}). Kept only for
* binary compatibility. See {@code Font#adjustFontSizeToFit}.
*/
@Deprecated
@Override
public TextStyle adjustFontSizeToFit(String text, TextStyle style, double availableWidth) {
return null;
Expand Down