Commit 5a91677d for tesseract
commit 5a91677d11b870b5eac021c7178feebe1c9f617c
Author: Stefan Weil <sw@weilnetz.de>
Date: Fri Nov 13 17:39:02 2026 +0100
api: run the recognition if only the layout analysis was done
GetHOCRText(), GetAltoText(), GetPAGEText(), GetTSVText(),
GetLSTMBoxText() and GetWordStrBoxText() only called Recognize() if
page_res_ was null. After AnalyseLayout(), which is all that
ProcessPage() does for tessedit_pageseg_mode 2 (PSM_AUTO_ONLY),
page_res_ exists without recognition results, and the iterator
dereferenced a null pointer.
Test recognition_done_ like GetUTF8Text(), GetBoxText() and
GetUNLVText() do.
The PDF renderer uses the iterator directly and crashed in the same
way, so it gets the same test. It needs to be a friend class of
TessBaseAPI for that.
This change needs the fix for the state of recognition_done_ on empty
pages first (pull request #4528). Without that fix Recognize() does
not set recognition_done_ for an empty page, so each of these
functions would run the layout analysis of an empty page again.
Fix proposed by the Anthropic OSS Scanner (finding ANT-2026-RBQXRM52).
Assisted-by: OpenCode / Claude Fable 5.2 (Anthropic)
Signed-off-by: Stefan Weil <sw@weilnetz.de>
diff --git a/include/tesseract/baseapi.h b/include/tesseract/baseapi.h
index 375c3907..1e0670d4 100644
--- a/include/tesseract/baseapi.h
+++ b/include/tesseract/baseapi.h
@@ -53,6 +53,7 @@ class ImageThresholder;
class LTRResultIterator;
class ResultIterator;
class MutableIterator;
+class TessPDFRenderer;
class TessResultRenderer;
class Tesseract;
@@ -74,6 +75,8 @@ using ProbabilityInContextFunc = double (Dict::*)(const char *, const char *,
* include any other Tesseract headers.
*/
class TESS_API TessBaseAPI {
+ friend class TessPDFRenderer;
+
public:
TessBaseAPI();
virtual ~TessBaseAPI();
diff --git a/src/api/altorenderer.cpp b/src/api/altorenderer.cpp
index 7225ddba..8e72aca2 100644
--- a/src/api/altorenderer.cpp
+++ b/src/api/altorenderer.cpp
@@ -154,7 +154,8 @@ char *TessBaseAPI::GetAltoText(int page_number) {
/// data structures.
///
char *TessBaseAPI::GetAltoText(ETEXT_DESC *monitor, int page_number) {
- if (tesseract_ == nullptr || (page_res_ == nullptr && Recognize(monitor) < 0)) {
+ if (tesseract_ == nullptr ||
+ ((page_res_ == nullptr || !recognition_done_) && Recognize(monitor) < 0)) {
return nullptr;
}
diff --git a/src/api/baseapi.cpp b/src/api/baseapi.cpp
index 676200e0..772d02f5 100644
--- a/src/api/baseapi.cpp
+++ b/src/api/baseapi.cpp
@@ -1367,7 +1367,8 @@ static void AddBoxToTSV(const PageIterator *it, PageIteratorLevel level, std::st
* Returned string must be freed with the delete [] operator.
*/
char *TessBaseAPI::GetTSVText(int page_number) {
- if (tesseract_ == nullptr || (page_res_ == nullptr && Recognize(nullptr) < 0)) {
+ if (tesseract_ == nullptr ||
+ ((page_res_ == nullptr || !recognition_done_) && Recognize(nullptr) < 0)) {
return nullptr;
}
diff --git a/src/api/hocrrenderer.cpp b/src/api/hocrrenderer.cpp
index 5f319eca..32da90e4 100644
--- a/src/api/hocrrenderer.cpp
+++ b/src/api/hocrrenderer.cpp
@@ -131,7 +131,8 @@ char *TessBaseAPI::GetHOCRText(int page_number) {
*/
char *TessBaseAPI::GetHOCRText(ETEXT_DESC *monitor, int page_number) {
if (tesseract_ == nullptr ||
- (page_res_ == nullptr && Recognize(monitor) < 0)) {
+ ((page_res_ == nullptr || !recognition_done_) &&
+ Recognize(monitor) < 0)) {
return nullptr;
}
diff --git a/src/api/lstmboxrenderer.cpp b/src/api/lstmboxrenderer.cpp
index 1514eeed..43c269c9 100644
--- a/src/api/lstmboxrenderer.cpp
+++ b/src/api/lstmboxrenderer.cpp
@@ -37,7 +37,8 @@ static void AddBoxToLSTM(int right, int bottom, int top, int image_height, int p
}
char *TessBaseAPI::GetLSTMBoxText(int page_number = 0) {
- if (tesseract_ == nullptr || (page_res_ == nullptr && Recognize(nullptr) < 0)) {
+ if (tesseract_ == nullptr ||
+ ((page_res_ == nullptr || !recognition_done_) && Recognize(nullptr) < 0)) {
return nullptr;
}
diff --git a/src/api/pagerenderer.cpp b/src/api/pagerenderer.cpp
index 6108045a..5ffa0296 100644
--- a/src/api/pagerenderer.cpp
+++ b/src/api/pagerenderer.cpp
@@ -712,7 +712,8 @@ char *TessBaseAPI::GetPAGEText(int page_number) {
///
char *TessBaseAPI::GetPAGEText(ETEXT_DESC *monitor, int /*page_number*/) {
if (tesseract_ == nullptr ||
- (page_res_ == nullptr && Recognize(monitor) < 0)) {
+ ((page_res_ == nullptr || !recognition_done_) &&
+ Recognize(monitor) < 0)) {
return nullptr;
}
diff --git a/src/api/pdfrenderer.cpp b/src/api/pdfrenderer.cpp
index 47f35185..5173b75b 100644
--- a/src/api/pdfrenderer.cpp
+++ b/src/api/pdfrenderer.cpp
@@ -825,6 +825,12 @@ bool TessPDFRenderer::imageToPDFObj(Pix *pix, const char *filename, long int obj
}
bool TessPDFRenderer::AddImageHandler(TessBaseAPI *api) {
+ // The text layer needs recognition results, but page_res_ may only hold
+ // layout analysis results here (ProcessPage skips recognition for
+ // PSM_AUTO_ONLY and PSM_OSD_ONLY), so run it like the Get*Text functions do.
+ if ((api->page_res_ == nullptr || !api->recognition_done_) && api->Recognize(nullptr) < 0) {
+ return false;
+ }
Pix *pix = api->GetInputImage();
const char *filename = api->GetInputName();
int ppi = api->GetSourceYResolution();
diff --git a/src/api/wordstrboxrenderer.cpp b/src/api/wordstrboxrenderer.cpp
index e4ffcedb..c9baf00a 100644
--- a/src/api/wordstrboxrenderer.cpp
+++ b/src/api/wordstrboxrenderer.cpp
@@ -30,7 +30,8 @@ namespace tesseract {
*/
char *TessBaseAPI::GetWordStrBoxText(int page_number = 0) {
- if (tesseract_ == nullptr || (page_res_ == nullptr && Recognize(nullptr) < 0)) {
+ if (tesseract_ == nullptr ||
+ ((page_res_ == nullptr || !recognition_done_) && Recognize(nullptr) < 0)) {
return nullptr;
}