90 lines
2.7 KiB
Go
90 lines
2.7 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/ocr"
|
|
"git.stormux.org/storm/cthulhu/internal/review"
|
|
)
|
|
|
|
// Retain an unchanged prefix or suffix exactly. A changed middle keeps the
|
|
// nearest legal offset; identical repeated text is deliberately not guessed.
|
|
func remapOCROffset(oldText, newText string, offset int) int {
|
|
oldRunes, newRunes := []rune(oldText), []rune(newText)
|
|
offset = max(0, min(offset, len(oldRunes)))
|
|
prefix := 0
|
|
for prefix < len(oldRunes) && prefix < len(newRunes) && oldRunes[prefix] == newRunes[prefix] {
|
|
prefix++
|
|
}
|
|
if offset < prefix {
|
|
return offset
|
|
}
|
|
suffix := 0
|
|
for suffix < len(oldRunes)-prefix && suffix < len(newRunes)-prefix &&
|
|
oldRunes[len(oldRunes)-1-suffix] == newRunes[len(newRunes)-1-suffix] {
|
|
suffix++
|
|
}
|
|
if offset >= len(oldRunes)-suffix {
|
|
return len(newRunes) - (len(oldRunes) - offset)
|
|
}
|
|
return min(offset, len(newRunes))
|
|
}
|
|
|
|
func (session *ocrSession) finish_refresh_locked(job ocrJob, interest review.Interest, capture ocr.Capture, result ocr.Result, err error) {
|
|
session.refreshing = false
|
|
if err != nil {
|
|
session.refreshFailed = true
|
|
session.status_locked("OCR refresh failed; activate after a successful refresh")
|
|
return
|
|
}
|
|
previous := session.cursor.Text()
|
|
offset := remapOCROffset(previous, result.Text, session.cursor.Offset())
|
|
anchor := -1
|
|
if session.selectionAnchor >= 0 {
|
|
anchor = remapOCROffset(previous, result.Text, session.selectionAnchor)
|
|
}
|
|
session.source, session.pixels = interest, capture
|
|
session.cursor = ocr.NewCursor(result)
|
|
position, _ := session.cursor.Set(offset)
|
|
session.selectionAnchor = anchor
|
|
session.refreshFailed = result.Text == ""
|
|
if result.Text == "" {
|
|
session.status_locked("No text recognized")
|
|
} else if result.Text != previous {
|
|
session.status_locked(ocrChangedAnnouncement(previous, result.Text))
|
|
} else if !job.automatic {
|
|
session.status_locked("OCR text unchanged")
|
|
}
|
|
if session.display != nil {
|
|
session.display.EnqueueFrame(session.frame_locked(position))
|
|
}
|
|
session.schedule_refresh_locked()
|
|
}
|
|
|
|
func ocrChangedAnnouncement(previous, current string) string {
|
|
oldRunes, newRunes := []rune(previous), []rune(current)
|
|
index := 0
|
|
for index < len(oldRunes) && index < len(newRunes) && oldRunes[index] == newRunes[index] {
|
|
index++
|
|
}
|
|
if index == len(newRunes) && index < len(oldRunes) {
|
|
return "OCR text removed"
|
|
}
|
|
start, end := index, index
|
|
for start > 0 && newRunes[start-1] != '\n' {
|
|
start--
|
|
}
|
|
for end < len(newRunes) && newRunes[end] != '\n' && end-start < 100 {
|
|
end++
|
|
}
|
|
line := strings.TrimSpace(string(newRunes[start:end]))
|
|
if line == "" {
|
|
return "OCR text changed"
|
|
}
|
|
return line
|
|
}
|