diff --git a/install.bat b/install.bat index 718a356..7df49e2 100644 --- a/install.bat +++ b/install.bat @@ -99,15 +99,15 @@ echo Installing tesserocr wheel... :: --- Smoke test: import key dependencies --- echo. echo Verifying dependencies... -"%PYTHON%" -c "import os,sys;[os.add_dll_directory(p) for p in [os.path.join(sys.prefix,'Library',d) for d in ['bin','mingw-w64\\bin','usr\\bin']] if os.path.isdir(p)];import cv2,mss,numpy,tesserocr,discord,transitions,rapidfuzz" >nul 2>&1 +"%PYTHON%" -c "import os,sys,ctypes;lb=os.path.join(sys.prefix,'Library','bin');[os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))];ctypes.WinDLL(os.path.join(lb,'tesseract51.dll')) if os.path.isfile(os.path.join(lb,'tesseract51.dll')) else None;import cv2,mss,numpy,tesserocr,discord,transitions,rapidfuzz" >nul 2>&1 if %errorlevel% neq 0 ( echo. echo WARNING: Dependency check failed. Running diagnostics... echo. - "%PYTHON%" -c "import os,sys; lb=os.path.join(sys.prefix,'Library','bin'); print(' Prefix:',sys.prefix); print(' Library/bin exists:',os.path.isdir(lb)); dlls=[f for f in (os.listdir(lb) if os.path.isdir(lb) else []) if any(k in f.lower() for k in ['tess','lepton'])]; print(' Tesseract DLLs found:',dlls if dlls else 'NONE - re-run install.bat'); [os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))]; __import__('tesserocr')" 2>&1 + "%PYTHON%" -c "import os,sys,ctypes; lb=os.path.join(sys.prefix,'Library','bin'); print(' Prefix:',sys.prefix); dlls=[f for f in (os.listdir(lb) if os.path.isdir(lb) else []) if any(k in f.lower() for k in ['tess','lepton'])]; print(' Tesseract DLLs in Library/bin:',dlls or 'NONE'); [os.add_dll_directory(os.path.join(sys.prefix,'Library',d)) for d in ['bin','mingw-w64\\bin','usr\\bin'] if os.path.isdir(os.path.join(sys.prefix,'Library',d))]; t=os.path.join(lb,'tesseract51.dll'); r=ctypes.WinDLL(t) if os.path.isfile(t) else None; print(' ctypes load tesseract51.dll:','OK' if r else 'skipped'); __import__('tesserocr'); print(' tesserocr import: OK')" 2>&1 echo. - echo If you see "NONE" above, re-run install.bat once more to let conda finish. - echo If the error persists, see development.md for manual steps. + echo *** Do NOT run "pip install tesserocr" manually - it builds from source and + echo *** will always fail on Windows. Re-run install.bat to fix dependencies. ) else ( echo All key dependencies verified. ) diff --git a/src/d2r_image/ocr.py b/src/d2r_image/ocr.py index d34e757..08ab186 100644 --- a/src/d2r_image/ocr.py +++ b/src/d2r_image/ocr.py @@ -1,7 +1,8 @@ import os import sys if os.name == 'nt': - # Python 3.8+ requires explicit DLL directories; conda puts tesseract/leptonica in Library\bin + import ctypes + # Add all conda DLL dirs so Python's loader can find them for _dll_dir in [ os.path.join(sys.prefix, 'Library', 'bin'), os.path.join(sys.prefix, 'Library', 'mingw-w64', 'bin'), @@ -9,6 +10,16 @@ if os.name == 'nt': ]: if os.path.isdir(_dll_dir): os.add_dll_directory(_dll_dir) + # Pre-load tesseract51.dll by absolute path so Windows anchors it to + # Library\bin when resolving its own transitive deps (leptonica, zlib, etc.). + # os.add_dll_directory alone is not enough: when Python loads the .pyd via + # LOAD_LIBRARY_SEARCH_USER_DIRS, Windows finds tesseract51.dll there, but + # then resolves tesseract's transitive deps using only System32 — not the + # user DLL dirs. Loading by absolute path first puts tesseract in the cache + # with Library\bin as its home, fixing the transitive search. + _tess = os.path.join(sys.prefix, 'Library', 'bin', 'tesseract51.dll') + if os.path.isfile(_tess): + ctypes.WinDLL(_tess) from tesserocr import PyTessBaseAPI, OEM import numpy as np import cv2 diff --git a/src/main.py b/src/main.py index 6fa9648..7d080cf 100644 --- a/src/main.py +++ b/src/main.py @@ -3,8 +3,8 @@ # This must run BEFORE any import that might trigger tesserocr loading. import os, sys if sys.platform == "win32": + import ctypes _conda_dll_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "conda_env", "Library", "bin") - # Also check the conda env where this python lives (sys.prefix is reliable on Windows) for _dll_candidate in [ _conda_dll_dir, os.path.join(sys.prefix, "Library", "bin"), @@ -13,6 +13,12 @@ if sys.platform == "win32": ]: if os.path.isdir(_dll_candidate): os.add_dll_directory(_dll_candidate) + # Pre-load tesseract51.dll by absolute path so Windows anchors its transitive + # deps (leptonica, zlib, etc.) to Library\bin. os.add_dll_directory alone is + # insufficient: transitive deps of a user-dir DLL are not searched there. + _tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll") + if os.path.isfile(_tess): + ctypes.WinDLL(_tess) from dataclasses import dataclass from input_layer import keyboard diff --git a/src/shopper.py b/src/shopper.py index 6d9d47a..40939d3 100644 --- a/src/shopper.py +++ b/src/shopper.py @@ -1,6 +1,7 @@ # Fix: On Windows, Python 3.8+ requires os.add_dll_directory for conda-forge DLLs import os, sys if sys.platform == "win32": + import ctypes for _dll_dir in [ os.path.join(sys.prefix, "Library", "bin"), os.path.join(sys.prefix, "Library", "mingw-w64", "bin"), @@ -8,6 +9,9 @@ if sys.platform == "win32": ]: if os.path.isdir(_dll_dir): os.add_dll_directory(_dll_dir) + _tess = os.path.join(sys.prefix, "Library", "bin", "tesseract51.dll") + if os.path.isfile(_tess): + ctypes.WinDLL(_tess) from beautifultable import BeautifulTable import logging