Commit 5cb5ac90 for tesseract

commit 5cb5ac9033e798256a6fb638520425632d4f1946
Author: Stefan Weil <sw@weilnetz.de>
Date:   Tue Nov 10 16:08:03 2026 +0100

    wordrec: do not output fake results of ambigs training

    In ambigs_classify_and_output() only check the ratings pointer, so a word
    skipped after the classification budget is used up is not always detected.
    A word with blobs is given a non-null ratings matrix full of fake space
    choices by SetupFake(); check the tess_failed flag, which SetupFake sets
    on every path, so that such fabricated results are not written to the
    ambigs output.

    In the unit test probe deu on its own: Init("eng+deu") succeeds if eng
    loads even when deu (a secondary language) fails to load, so the
    multilingual assertion would then run against English alone and fail
    spuriously instead of being skipped.

    Assisted-by: OpenCode / qwen3.8-27b-thinking (Alibaba Cloud)
    Signed-off-by: Stefan Weil <sw@weilnetz.de>

diff --git a/src/ccmain/recogtraining.cpp b/src/ccmain/recogtraining.cpp
index 4669cfe9..0a6d0af1 100644
--- a/src/ccmain/recogtraining.cpp
+++ b/src/ccmain/recogtraining.cpp
@@ -220,9 +220,11 @@ void Tesseract::ambigs_classify_and_output(const char *label, PAGE_RES_IT *pr_it
     return;
   }

-  if (werd_res->ratings == nullptr) {
-    // The word was not recognized (for example because the classification
-    // budget was used up), so there are no paths to output.
+  if (werd_res->tess_failed || werd_res->ratings == nullptr) {
+    // The word was not really recognized. When the classification budget is
+    // used up, SetupFake() gives a word with blobs a non-null ratings matrix
+    // full of fake space choices; the tess_failed flag (set by SetupFake)
+    // catches that case, while a word without blobs keeps a null ratings.
     return;
   }

diff --git a/unittest/baseapi_test.cc b/unittest/baseapi_test.cc
index f927d585..f10caa80 100644
--- a/unittest/baseapi_test.cc
+++ b/unittest/baseapi_test.cc
@@ -159,7 +159,10 @@ TEST_F(TesseractTest, ClassificationBudgetTest) {
   // The budget is per language, so a second language without limit takes
   // over when the budget of the first language is used up.
   REQUIRE_TRAINEDDATA(TESSDATA_DIR, "deu");
-  if (!has_legacy_model("eng+deu")) {
+  // Probe deu on its own: Init("eng+deu") would succeed if eng loads even
+  // when deu (a secondary language) fails, so the multilingual test below
+  // would run against English alone and fail spuriously.
+  if (!has_legacy_model("deu")) {
     // deu.traineddata without legacy model.
     GTEST_SKIP();
   }