text: /**********************************************************************
   * File:        tesseract.cpp
   * Description: Main program for merge of tess and editor.
   * Author:      Ray Smith
   *
   * (C) Copyright 1992, Hewlett-Packard Ltd.
   ** Licensed under the Apache License, Version 2.0 (the "License");
   ** you may not use this file except in compliance with the License.
   ** You may obtain a copy of the License at
   ** http://www.apache.org/licenses/LICENSE-2.0
   ** Unless required by applicable law or agreed to in writing, software
   ** distributed under the License is distributed on an "AS IS" BASIS,
   ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ** See the License for the specific language governing permissions and
   ** limitations under the License.
   *
   **********************************************************************/
  
  // Include automatically generated configuration file if running autoconf
  #ifdef HAVE_CONFIG_H
  #  include "config_auto.h"
  #endif
  
  #include <cerrno> // for errno
  #if defined(__USE_GNU)
  #  include <cfenv> // for feenableexcept
  #endif
  #include <climits> // for INT_MIN, INT_MAX
  #include <cstdlib> // for std::getenv
  #include <iostream>
  #include <map>    // for std::map
  #include <memory> // std::unique_ptr
  
  #include <allheaders.h>
  #include <tesseract/baseapi.h>
  #include "dict.h"
  #if defined(USE_OPENCL)
  #  include "openclwrapper.h" // for OpenclDevice
  #endif
  #include <tesseract/renderer.h>
  #include "simddetect.h"
  #include "tprintf.h" // for tprintf
  
  #ifdef _OPENMP
  #  include <omp.h>
  #endif
  
  #if defined(HAVE_LIBARCHIVE)
  #  include <archive.h>
  #endif
  #if defined(HAVE_LIBCURL)
  #  include <curl/curl.h>
  #endif
  
  #if defined(_WIN32)
  #  include <fcntl.h>
  #  include <io.h>
  #  if defined(HAVE_TIFFIO_H)
  
  #    include <tiffio.h>
  
  static void Win32ErrorHandler(const char *module, const char *fmt, va_list ap) {
    if (module != nullptr) {
      fprintf(stderr, "%s: ", module);
    }
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, ".\n");
  }
  
  static void Win32WarningHandler(const char *module, const char *fmt, va_list ap) {
    if (module != nullptr) {
      fprintf(stderr, "%s: ", module);
    }
    fprintf(stderr, "Warning, ");
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, ".\n");
  }
  
  #  endif /* HAVE_TIFFIO_H */
  
  class AutoWin32ConsoleOutputCP {
  public:
    explicit AutoWin32ConsoleOutputCP(UINT codeCP) {
      oldCP_ = GetConsoleOutputCP();
      SetConsoleOutputCP(codeCP);
    }
    ~AutoWin32ConsoleOutputCP() {
      SetConsoleOutputCP(oldCP_);
    }
  
  private:
    UINT oldCP_;
  };
  
  static AutoWin32ConsoleOutputCP autoWin32ConsoleOutputCP(CP_UTF8);
  
  #endif // _WIN32
  
  using namespace tesseract;
  
  static void PrintVersionInfo() {
    char *versionStrP;
  
    printf("tesseract %s\n", tesseract::TessBaseAPI::Version());
  
    versionStrP = getLeptonicaVersion();
    printf(" %s\n", versionStrP);
    lept_free(versionStrP);
  
    versionStrP = getImagelibVersions();
    printf("  %s\n", versionStrP);
    lept_free(versionStrP);
  
  #ifdef USE_OPENCL
    cl_platform_id platform[4];
    cl_uint num_platforms;
  
    printf(" OpenCL info:\n");
    if (clGetPlatformIDs(4, platform, &num_platforms) == CL_SUCCESS) {
      printf("  Found %u platform(s).\n", num_platforms);
      for (unsigned n = 0; n < num_platforms; n++) {
        char info[256];
        if (clGetPlatformInfo(platform[n], CL_PLATFORM_NAME, 256, info, 0) == CL_SUCCESS) {
          printf("  Platform %u name: %s.\n", n + 1, info);
        }
        if (clGetPlatformInfo(platform[n], CL_PLATFORM_VERSION, 256, info, 0) == CL_SUCCESS) {
          printf("  Version: %s.\n", info);
        }
        cl_device_id devices[2];
        cl_uint num_devices;
        if (clGetDeviceIDs(platform[n], CL_DEVICE_TYPE_ALL, 2, devices, &num_devices) == CL_SUCCESS) {
          printf("  Found %u device(s).\n", num_devices);
          for (unsigned i = 0; i < num_devices; ++i) {
            if (clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 256, info, 0) == CL_SUCCESS) {
              printf("    Device %u name: %s.\n", i + 1, info);
            }
          }
        }
      }
    }
  #endif
  #if defined(HAVE_NEON) || defined(__aarch64__)
    if (tesseract::SIMDDetect::IsNEONAvailable())
      printf(" Found NEON\n");
  #else
    if (tesseract::SIMDDetect::IsAVX512BWAvailable()) {
      printf(" Found AVX512BW\n");
    }
    if (tesseract::SIMDDetect::IsAVX512FAvailable()) {
      printf(" Found AVX512F\n");
    }
    if (tesseract::SIMDDetect::IsAVX2Available()) {
      printf(" Found AVX2\n");
    }
    if (tesseract::SIMDDetect::IsAVXAvailable()) {
      printf(" Found AVX\n");
    }
    if (tesseract::SIMDDetect::IsFMAAvailable()) {
      printf(" Found FMA\n");
    }
    if (tesseract::SIMDDetect::IsSSEAvailable()) {
      printf(" Found SSE4.1\n");
    }
  #endif
  #ifdef _OPENMP
    printf(" Found OpenMP %d\n", _OPENMP);
  #endif
  #if defined(HAVE_LIBARCHIVE)
  #  if ARCHIVE_VERSION_NUMBER >= 3002000
    printf(" Found %s\n", archive_version_details());
  #  else
    printf(" Found %s\n", archive_version_string());
  #  endif // ARCHIVE_VERSION_NUMBER
  #endif   // HAVE_LIBARCHIVE
  #if defined(HAVE_LIBCURL)
    printf(" Found %s\n", curl_version());
  #endif
  }
  
  static void PrintHelpForPSM() {
    const char *msg =
        "Page segmentation modes:\n"
        "  0    Orientation and script detection (OSD) only.\n"
        "  1    Automatic page segmentation with OSD.\n"
        "  2    Automatic page segmentation, but no OSD, or OCR. (not "
        "implemented)\n"
        "  3    Fully automatic page segmentation, but no OSD. (Default)\n"
        "  4    Assume a single column of text of variable sizes.\n"
        "  5    Assume a single uniform block of vertically aligned text.\n"
        "  6    Assume a single uniform block of text.\n"
        "  7    Treat the image as a single text line.\n"
        "  8    Treat the image as a single word.\n"
        "  9    Treat the image as a single word in a circle.\n"
        " 10    Treat the image as a single character.\n"
        " 11    Sparse text. Find as much text as possible in no"
        " particular order.\n"
        " 12    Sparse text with OSD.\n"
        " 13    Raw line. Treat the image as a single text line,\n"
        "       bypassing hacks that are Tesseract-specific.\n";
  
  #ifdef DISABLED_LEGACY_ENGINE
    const char *disabled_osd_msg = "\nNOTE: The OSD modes are currently disabled.\n";
    printf("%s%s", msg, disabled_osd_msg);
  #else
    printf("%s", msg);
  #endif
  }
  
  #ifndef DISABLED_LEGACY_ENGINE
  static void PrintHelpForOEM() {
    const char *msg =
        "OCR Engine modes:\n"
        "  0    Legacy engine only.\n"
        "  1    Neural nets LSTM engine only.\n"
        "  2    Legacy + LSTM engines.\n"
        "  3    Default, based on what is available.\n";
  
    printf("%s", msg);
  }
  #endif // ndef DISABLED_LEGACY_ENGINE
  
  static void PrintHelpExtra(const char *program) {
    printf(
        "Usage:\n"
        "  %s --help | --help-extra | --help-psm | "
  #ifndef DISABLED_LEGACY_ENGINE
        "--help-oem | "
  #endif
        "--version\n"
        "  %s --list-langs [--tessdata-dir PATH]\n"
  #ifndef DISABLED_LEGACY_ENGINE
        "  %s --print-fonts-table [options...] [configfile...]\n"
  #endif  // ndef DISABLED_LEGACY_ENGINE
        "  %s --print-parameters [options...] [configfile...]\n"
        "  %s imagename|imagelist|stdin outputbase|stdout [options...] "
        "[configfile...]\n"
        "\n"
        "OCR options:\n"
        "  --tessdata-dir PATH   Specify the location of tessdata path.\n"
        "  --user-words PATH     Specify the location of user words file.\n"
        "  --user-patterns PATH  Specify the location of user patterns file.\n"
        "  --dpi VALUE           Specify DPI for input image.\n"
        "  --loglevel LEVEL      Specify logging level. LEVEL can be\n"
        "                        ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL or OFF.\n"
        "  -l LANG[+LANG]        Specify language(s) used for OCR.\n"
        "  -c VAR=VALUE          Set value for config variables.\n"
        "                        Multiple -c arguments are allowed.\n"
        "  --psm NUM             Specify page segmentation mode.\n"
  #ifndef DISABLED_LEGACY_ENGINE
        "  --oem NUM             Specify OCR Engine mode.\n"
  #endif
        "NOTE: These options must occur before any configfile.\n"
        "\n",
        program, program, program, program
  #ifndef DISABLED_LEGACY_ENGINE
        , program
  #endif  // ndef DISABLED_LEGACY_ENGINE
    );
  
    PrintHelpForPSM();
  #ifndef DISABLED_LEGACY_ENGINE
    printf("\n");
    PrintHelpForOEM();
  #endif
  
    printf(
        "\n"
        "Single options:\n"
        "  -h, --help            Show minimal help message.\n"
        "  --help-extra          Show extra help for advanced users.\n"
        "  --help-psm            Show page segmentation modes.\n"
  #ifndef DISABLED_LEGACY_ENGINE
        "  --help-oem            Show OCR Engine modes.\n"
  #endif
        "  -v, --version         Show version information.\n"
        "  --list-langs          List available languages for tesseract engine.\n"
  #ifndef DISABLED_LEGACY_ENGINE
        "  --print-fonts-table   Print tesseract fonts table.\n"
  #endif  // ndef DISABLED_LEGACY_ENGINE
        "  --print-parameters    Print tesseract parameters.\n");
  }
  
  static void PrintHelpMessage(const char *program) {
    printf(
        "Usage:\n"
        "  %s --help | --help-extra | --version\n"
        "  %s --list-langs\n"
        "  %s imagename outputbase [options...] [configfile...]\n"
        "\n"
        "OCR options:\n"
        "  -l LANG[+LANG]        Specify language(s) used for OCR.\n"
        "NOTE: These options must occur before any configfile.\n"
        "\n"
        "Single options:\n"
        "  --help                Show this help message.\n"
        "  --help-extra          Show extra help for advanced users.\n"
        "  --version             Show version information.\n"
        "  --list-langs          List available languages for tesseract "
        "engine.\n",
        program, program, program);
  }
  
  static bool SetVariablesFromCLArgs(tesseract::TessBaseAPI &api, int argc, char **argv) {
    bool success = true;
    char opt1[256], opt2[255];
    for (int i = 0; i < argc; i++) {
      if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) {
        strncpy(opt1, argv[i + 1], 255);
        opt1[255] = '\0';
        char *p = strchr(opt1, '=');
        if (!p) {
          fprintf(stderr, "Missing = in configvar assignment\n");
          success = false;
          break;
        }
        *p = 0;
        strncpy(opt2, strchr(argv[i + 1], '=') + 1, sizeof(opt2) - 1);
        opt2[254] = 0;
        ++i;
  
        if (!api.SetVariable(opt1, opt2)) {
          fprintf(stderr, "Could not set option: %s=%s\n", opt1, opt2);
        }
      }
    }
    return success;
  }
  
  static void PrintLangsList(tesseract::TessBaseAPI &api) {
    std::vector<std::string> languages;
    api.GetAvailableLanguagesAsVector(&languages);
    printf("List of available languages in \"%s\" (%zu):\n",
           api.GetDatapath(), languages.size());
    for (const auto &language : languages) {
      printf("%s\n", language.c_str());
    }
    api.End();
  }
  
  /**
   * We have 2 possible sources of pagesegmode: a config file and
   * the command line. For backwards compatibility reasons, the
   * default in tesseract is tesseract::PSM_SINGLE_BLOCK, but the
   * default for this program is tesseract::PSM_AUTO. We will let
   * the config file take priority, so the command-line default
   * can take priority over the tesseract default, so we use the
   * value from the command line only if the retrieved mode
   * is still tesseract::PSM_SINGLE_BLOCK, indicating no change
   * in any config file. Therefore the only way to force
   * tesseract::PSM_SINGLE_BLOCK is from the command line.
   * It would be simpler if we could set the value before Init,
   * but that doesn't work.
   */
  static void FixPageSegMode(tesseract::TessBaseAPI &api, tesseract::PageSegMode pagesegmode) {
    if (api.GetPageSegMode() == tesseract::PSM_SINGLE_BLOCK) {
      api.SetPageSegMode(pagesegmode);
    }
  }
  
  static bool checkArgValues(int arg, const char *mode, int count) {
    if (arg >= count || arg < 0) {
      printf("Invalid %s value, please enter a number between 0-%d\n", mode, count - 1);
      return false;
    }
    return true;
  }
  
  // NOTE: arg_i is used here to avoid ugly *i so many times in this function
  static bool ParseArgs(int argc, char **argv, const char **lang, const char **image,
                        const char **outputbase, const char **datapath, l_int32 *dpi,
                        bool *list_langs, bool *print_parameters, bool* print_fonts_table, std::vector<std::string> *vars_vec,
                        std::vector<std::string> *vars_values, l_int32 *arg_i,
                        tesseract::PageSegMode *pagesegmode, tesseract::OcrEngineMode *enginemode) {
    bool noocr = false;
    int i;
    for (i = 1; i < argc && (*outputbase == nullptr || argv[i][0] == '-'); i++) {
      if (*image != nullptr && *outputbase == nullptr) {
        // outputbase follows image, don't allow options at that position.
        *outputbase = argv[i];
      } else if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) {
        PrintHelpMessage(argv[0]);
        noocr = true;
      } else if (strcmp(argv[i], "--help-extra") == 0) {
        PrintHelpExtra(argv[0]);
        noocr = true;
      } else if ((strcmp(argv[i], "--help-psm") == 0)) {
        PrintHelpForPSM();
        noocr = true;
  #ifndef DISABLED_LEGACY_ENGINE
      } else if ((strcmp(argv[i], "--help-oem") == 0)) {
        PrintHelpForOEM();
        noocr = true;
  #endif
      } else if ((strcmp(argv[i], "-v") == 0) || (strcmp(argv[i], "--version") == 0)) {
        PrintVersionInfo();
        noocr = true;
      } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
        *lang = argv[i + 1];
        ++i;
      } else if (strcmp(argv[i], "--tessdata-dir") == 0 && i + 1 < argc) {
        *datapath = argv[i + 1];
        ++i;
      } else if (strcmp(argv[i], "--dpi") == 0 && i + 1 < argc) {
        *dpi = atoi(argv[i + 1]);
        ++i;
      } else if (strcmp(argv[i], "--loglevel") == 0 && i + 1 < argc) {
        // Allow the log levels which are used by log4cxx.
        const std::string loglevel_string = argv[++i];
        static const std::map<const std::string, int> loglevels {
          {"ALL", INT_MIN},
          {"TRACE", 5000},
          {"DEBUG", 10000},
          {"INFO", 20000},
          {"WARN", 30000},
          {"ERROR", 40000},
          {"FATAL", 50000},
          {"OFF", INT_MAX},
        };
        try {
          auto loglevel = loglevels.at(loglevel_string);
  	log_level = loglevel;
        } catch(const std::out_of_range& e) {
          // TODO: Allow numeric argument?
  	tprintf("Error, unsupported --loglevel %s\n", loglevel_string.c_str());
          return false;
        }
      } else if (strcmp(argv[i], "--user-words") == 0 && i + 1 < argc) {
        vars_vec->push_back("user_words_file");
        vars_values->push_back(argv[i + 1]);
        ++i;
      } else if (strcmp(argv[i], "--user-patterns") == 0 && i + 1 < argc) {
        vars_vec->push_back("user_patterns_file");
        vars_values->push_back(argv[i + 1]);
        ++i;
      } else if (strcmp(argv[i], "--list-langs") == 0) {
        noocr = true;
        *list_langs = true;
      } else if (strcmp(argv[i], "--psm") == 0 && i + 1 < argc) {
        if (!checkArgValues(atoi(argv[i + 1]), "PSM", tesseract::PSM_COUNT)) {
          return false;
        }
        *pagesegmode = static_cast<tesseract::PageSegMode>(atoi(argv[i + 1]));
        ++i;
      } else if (strcmp(argv[i], "--oem") == 0 && i + 1 < argc) {
  #ifndef DISABLED_LEGACY_ENGINE
        int oem = atoi(argv[i + 1]);
        if (!checkArgValues(oem, "OEM", tesseract::OEM_COUNT)) {
          return false;
        }
        *enginemode = static_cast<tesseract::OcrEngineMode>(oem);
  #endif
        ++i;
      } else if (strcmp(argv[i], "--print-parameters") == 0) {
        noocr = true;
        *print_parameters = true;
  #ifndef DISABLED_LEGACY_ENGINE
      } else if (strcmp(argv[i], "--print-fonts-table") == 0) {
        noocr = true;
        *print_fonts_table = true;
  #endif  // ndef DISABLED_LEGACY_ENGINE
      } else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) {
        // handled properly after api init
        ++i;
      } else if (*image == nullptr) {
        *image = argv[i];
      } else {
        // Unexpected argument.
        fprintf(stderr, "Error, unknown command line argument '%s'\n", argv[i]);
        return false;
      }
    }
  
    *arg_i = i;
  
    if (*pagesegmode == tesseract::PSM_OSD_ONLY) {
      // OSD = orientation and script detection.
      if (*lang != nullptr && strcmp(*lang, "osd")) {
        // If the user explicitly specifies a language (other than osd)
        // or a script, only orientation can be detected.
        fprintf(stderr, "Warning, detects only orientation with -l %s\n", *lang);
      } else {
        // That mode requires osd.traineddata to detect orientation and script.
        *lang = "osd";
      }
    }
  
    if (*outputbase == nullptr && noocr == false) {
      PrintHelpMessage(argv[0]);
      return false;
    }
  
    return true;
  }
  
  static void PreloadRenderers(tesseract::TessBaseAPI &api,
                               std::vector<std::unique_ptr<TessResultRenderer>> &renderers,
                               tesseract::PageSegMode pagesegmode, const char *outputbase) {
    if (pagesegmode == tesseract::PSM_OSD_ONLY) {
  #ifndef DISABLED_LEGACY_ENGINE
      renderers.push_back(std::make_unique<tesseract::TessOsdRenderer>(outputbase));
  #endif // ndef DISABLED_LEGACY_ENGINE
    } else {
      bool error = false;
      bool b;
      api.GetBoolVariable("tessedit_create_hocr", &b);
      if (b) {
        bool font_info;
        api.GetBoolVariable("hocr_font_info", &font_info);
        auto renderer = std::make_unique<tesseract::TessHOcrRenderer>(outputbase, font_info);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create hOCR output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_alto", &b);
      if (b) {
        auto renderer = std::make_unique<tesseract::TessAltoRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create ALTO output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_tsv", &b);
      if (b) {
        bool font_info;
        api.GetBoolVariable("hocr_font_info", &font_info);
        auto renderer = std::make_unique<tesseract::TessTsvRenderer>(outputbase, font_info);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create TSV output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_pdf", &b);
      if (b) {
  #ifdef WIN32
        if (_setmode(_fileno(stdout), _O_BINARY) == -1)
          tprintf("ERROR: cin to binary: %s", strerror(errno));
  #endif // WIN32
        bool textonly;
        api.GetBoolVariable("textonly_pdf", &textonly);
        auto renderer = std::make_unique<tesseract::TessPDFRenderer>(outputbase, api.GetDatapath(), textonly);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create PDF output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_write_unlv", &b);
      if (b) {
        api.SetVariable("unlv_tilde_crunching", "true");
        auto renderer = std::make_unique<tesseract::TessUnlvRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create UNLV output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_lstmbox", &b);
      if (b) {
        auto renderer = std::make_unique<tesseract::TessLSTMBoxRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create LSTM BOX output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_boxfile", &b);
      if (b) {
        auto renderer = std::make_unique<tesseract::TessBoxTextRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create BOX output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_wordstrbox", &b);
      if (b) {
        auto renderer = std::make_unique<tesseract::TessWordStrBoxRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create WordStr BOX output file: %s\n", strerror(errno));
          error = true;
        }
      }
  
      api.GetBoolVariable("tessedit_create_txt", &b);
      if (b || (!error && renderers.empty())) {
        // Create text output if no other output was requested
        // even if text output was not explicitly requested unless
        // there was an error.
        auto renderer = std::make_unique<tesseract::TessTextRenderer>(outputbase);
        if (renderer->happy()) {
          renderers.push_back(std::move(renderer));
        } else {
          tprintf("Error, could not create TXT output file: %s\n", strerror(errno));
        }
      }
    }
  
    // Null-out the renderers that are
    // added to the root, and leave the root in the vector.
    for (size_t r = 1; r < renderers.size(); ++r) {
      renderers[0]->insert(renderers[r].get());
      renderers[r].release(); // at the moment insert() is owning
    }
  }
  
  /**********************************************************************
   *  main()
   *
   **********************************************************************/
  
  int main(int argc, char **argv) {
  #if defined(__USE_GNU)
    // Raise SIGFPE.
  #  if defined(__clang__)
    // clang creates code which causes some FP exceptions, so don't enable those.
    feenableexcept(FE_DIVBYZERO);
  #  else
    feenableexcept(FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID);
  #  endif
  #endif
    const char *lang = nullptr;
    const char *image = nullptr;
    const char *outputbase = nullptr;
    const char *datapath = nullptr;
    bool list_langs = false;
    bool print_parameters = false;
    bool print_fonts_table = false;
    l_int32 dpi = 0;
    int arg_i = 1;
    tesseract::PageSegMode pagesegmode = tesseract::PSM_AUTO;
  #ifdef DISABLED_LEGACY_ENGINE
    auto enginemode = tesseract::OEM_LSTM_ONLY;
  #else
    tesseract::OcrEngineMode enginemode = tesseract::OEM_DEFAULT;
  #endif
    std::vector<std::string> vars_vec;
    std::vector<std::string> vars_values;
  
    if (std::getenv("LEPT_MSG_SEVERITY")) {
      // Get Leptonica message level from environment variable.
      setMsgSeverity(L_SEVERITY_EXTERNAL);
    } else {
      // Disable debugging and informational messages from Leptonica.
      setMsgSeverity(L_SEVERITY_ERROR);
    }
  
  #if defined(HAVE_TIFFIO_H) && defined(_WIN32)
    /* Show libtiff errors and warnings on console (not in GUI). */
    TIFFSetErrorHandler(Win32ErrorHandler);
    TIFFSetWarningHandler(Win32WarningHandler);
  #endif // HAVE_TIFFIO_H && _WIN32
  
    if (!ParseArgs(argc, argv, &lang, &image, &outputbase, &datapath, &dpi, &list_langs,
                   &print_parameters, &print_fonts_table, &vars_vec, &vars_values, &arg_i, &pagesegmode, &enginemode)) {
      return EXIT_FAILURE;
    }
  
    bool in_recognition_mode = !list_langs && !print_parameters && !print_fonts_table;
  
    if (lang == nullptr && in_recognition_mode) {
      // Set default language model if none was given and a model file is needed.
      lang = "eng";
    }
  
    if (image == nullptr && in_recognition_mode) {
      return EXIT_SUCCESS;
    }
  
    // Call GlobalDawgCache here to create the global DawgCache object before
    // the TessBaseAPI object. This fixes the order of destructor calls:
    // first TessBaseAPI must be destructed, DawgCache must be the last object.
    tesseract::Dict::GlobalDawgCache();
  
    TessBaseAPI api;
  
    api.SetOutputName(outputbase);
  
    const int init_failed = api.Init(datapath, lang, enginemode, &(argv[arg_i]), argc - arg_i,
                                     &vars_vec, &vars_values, false);
  
    if (!SetVariablesFromCLArgs(api, argc, argv)) {
      return EXIT_FAILURE;
    }
  
    // SIMD settings might be overridden by config variable.
    tesseract::SIMDDetect::Update();
  
    if (list_langs) {
      PrintLangsList(api);
      return EXIT_SUCCESS;
    }
  
    if (init_failed) {
      fprintf(stderr, "Could not initialize tesseract.\n");
      return EXIT_FAILURE;
    }
  
    if (print_parameters) {
      FILE *fout = stdout;
      fprintf(stdout, "Tesseract parameters:\n");
      api.PrintVariables(fout);
      api.End();
      return EXIT_SUCCESS;
    }
  
  #ifndef DISABLED_LEGACY_ENGINE
    if (print_fonts_table) {
      FILE* fout = stdout;
      fprintf(stdout, "Tesseract fonts table:\n");
      api.PrintFontsTable(fout);
      api.End();
      return EXIT_SUCCESS;
    }
  #endif  // ndef DISABLED_LEGACY_ENGINE
  
    FixPageSegMode(api, pagesegmode);
  
    if (dpi) {
      auto dpi_string = std::to_string(dpi);
      api.SetVariable("user_defined_dpi", dpi_string.c_str());
    }
  
    int ret_val = EXIT_SUCCESS;
  
    if (pagesegmode == tesseract::PSM_AUTO_ONLY) {
      Pix *pixs = pixRead(image);
      if (!pixs) {
        fprintf(stderr, "Leptonica can't process input file: %s\n", image);
        return 2;
      }
  
      api.SetImage(pixs);
  
      tesseract::Orientation orientation;
      tesseract::WritingDirection direction;
      tesseract::TextlineOrder order;
      float deskew_angle;
  
      const std::unique_ptr<const tesseract::PageIterator> it(api.AnalyseLayout());
      if (it) {
        // TODO: Implement output of page segmentation, see documentation
        // ("Automatic page segmentation, but no OSD, or OCR").
        it->Orientation(&orientation, &direction, &order, &deskew_angle);
        tprintf(
            "Orientation: %d\nWritingDirection: %d\nTextlineOrder: %d\n"
            "Deskew angle: %.4f\n",
            orientation, direction, order, deskew_angle);
      } else {
        ret_val = EXIT_FAILURE;
      }
  
      pixDestroy(&pixs);
      return ret_val;
    }
  
    // Set in_training_mode to true when using one of these configs:
    // ambigs.train, box.train, box.train.stderr, linebox, rebox, lstm.train.
    // In this mode no other OCR result files are written.
    bool b = false;
    bool in_training_mode = (api.GetBoolVariable("tessedit_ambigs_training", &b) && b) ||
                            (api.GetBoolVariable("tessedit_resegment_from_boxes", &b) && b) ||
                            (api.GetBoolVariable("tessedit_make_boxes_from_boxes", &b) && b) ||
                            (api.GetBoolVariable("tessedit_train_line_recognizer", &b) && b);
  
  #ifdef DISABLED_LEGACY_ENGINE
    auto cur_psm = api.GetPageSegMode();
    auto osd_warning = std::string("");
    if (cur_psm == tesseract::PSM_OSD_ONLY) {
      const char *disabled_osd_msg =
          "\nERROR: The page segmentation mode 0 (OSD Only) is currently "
          "disabled.\n\n";
      fprintf(stderr, "%s", disabled_osd_msg);
      return EXIT_FAILURE;
    } else if (cur_psm == tesseract::PSM_AUTO_OSD) {
      api.SetPageSegMode(tesseract::PSM_AUTO);
      osd_warning +=
          "\nWarning: The page segmentation mode 1 (Auto+OSD) is currently "
          "disabled. "
          "Using PSM 3 (Auto) instead.\n\n";
    } else if (cur_psm == tesseract::PSM_SPARSE_TEXT_OSD) {
      api.SetPageSegMode(tesseract::PSM_SPARSE_TEXT);
      osd_warning +=
          "\nWarning: The page segmentation mode 12 (Sparse text + OSD) is "
          "currently disabled. "
          "Using PSM 11 (Sparse text) instead.\n\n";
    }
  #endif // def DISABLED_LEGACY_ENGINE
  
    std::vector<std::unique_ptr<TessResultRenderer>> renderers;
  
    if (in_training_mode) {
      renderers.push_back(nullptr);
    } else if (outputbase != nullptr) {
      PreloadRenderers(api, renderers, pagesegmode, outputbase);
    }
  
    if (!renderers.empty()) {
  #ifdef DISABLED_LEGACY_ENGINE
      if (!osd_warning.empty()) {
        fprintf(stderr, "%s", osd_warning.c_str());
      }
  #endif
      bool succeed = api.ProcessPages(image, nullptr, 0, renderers[0].get());
      if (!succeed) {
        fprintf(stderr, "Error during processing.\n");
        ret_val = EXIT_FAILURE;
      }
    }
  
    return ret_val;
  }
  