diff --git a/admin-lehrer/components/ocr-overlay/OverlayReconstruction.tsx b/admin-lehrer/components/ocr-overlay/OverlayReconstruction.tsx
index 3161926..2b712f5 100644
--- a/admin-lehrer/components/ocr-overlay/OverlayReconstruction.tsx
+++ b/admin-lehrer/components/ocr-overlay/OverlayReconstruction.tsx
@@ -316,6 +316,14 @@ export function OverlayReconstruction({ sessionId, onNext }: OverlayReconstructi
const imgH = imageNaturalSize?.h || 1
const containerH = reconWidth * (imgH / imgW)
+ // Compute median cell height (in px) for consistent font sizing
+ const medianCellHeightPx = useMemo(() => {
+ if (cells.length === 0) return 40
+ const heights = cells.map(c => containerH * (c.bboxPct.h / 100)).sort((a, b) => a - b)
+ const mid = Math.floor(heights.length / 2)
+ return heights.length % 2 === 0 ? (heights[mid - 1] + heights[mid]) / 2 : heights[mid]
+ }, [cells, containerH])
+
return (
{/* Toolbar */}
@@ -460,13 +468,12 @@ export function OverlayReconstruction({ sessionId, onNext }: OverlayReconstructi
const edited = isEdited(cell)
const wordPos = cellWordPositions.get(cell.cellId)
const bboxPct = cell.bboxPct
- const cellHeightPx = containerH * (bboxPct.h / 100)
const colorValue = textColor === 'black' ? '#1a1a1a' : textColor
// Pixel-analysed: render word-groups at detected positions
if (wordPos && wordPos.length > 0) {
return wordPos.map((wp, i) => {
- const autoFontPx = cellHeightPx * wp.fontRatio * fontScale
+ const autoFontPx = medianCellHeightPx * wp.fontRatio * fontScale
const fs = Math.max(6, autoFontPx)
if (wordPos.length > 1) {
@@ -536,7 +543,7 @@ export function OverlayReconstruction({ sessionId, onNext }: OverlayReconstructi
// Fallback: no pixel data — single input at cell bbox
if (!cell.text) return null
- const fontSize = Math.max(6, cellHeightPx * fontScale)
+ const fontSize = Math.max(6, medianCellHeightPx * fontScale)
return (
(c.end - c.start + 1) > minClusterW)
+ if (clusters.length === 0) continue
+
if (rotation === 180) {
clusters = clusters.map(c => ({
start: cw - 1 - c.end,