Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/tesseract/src/viewer/svutil.cpp @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/tesseract/src/viewer/svutil.cpp Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,359 @@ +/////////////////////////////////////////////////////////////////////// +// File: svutil.cpp +// Description: ScrollView Utilities +// Author: Joern Wanke +// +// (C) Copyright 2007, Google Inc. +// 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. +// +/////////////////////////////////////////////////////////////////////// +// +// SVUtil contains the SVSync and SVNetwork classes, which are used for +// thread/process creation & synchronization and network connection. + +// Include automatically generated configuration file if running autoconf. +#ifdef HAVE_CONFIG_H +# include "config_auto.h" +#endif + +#include "svutil.h" + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <memory> +#include <string> +#include <thread> // for std::this_thread +#include <vector> + +#ifdef _WIN32 +# pragma comment(lib, "Ws2_32.lib") +# include <winsock2.h> // for fd_set, send, .. +# include <ws2tcpip.h> // for addrinfo +#else +# include <arpa/inet.h> +# include <netdb.h> +# include <netinet/in.h> +# include <semaphore.h> +# include <sys/select.h> +# include <sys/socket.h> +# include <csignal> +# ifdef __linux__ +# include <sys/prctl.h> +# endif +# include <unistd.h> +#endif + +#if defined(_WIN32) && !defined(__GNUC__) +# define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr) +#endif /* _WIN32 && !__GNUC__ */ + +#ifndef GRAPHICS_DISABLED + +namespace tesseract { + +const int kMaxMsgSize = 4096; + +// Starts a new process. +void SVSync::StartProcess(const char *executable, const char *args) { + std::string proc; + proc.append(executable); + proc.append(" "); + proc.append(args); + std::cout << "Starting " << proc << std::endl; +# ifdef _WIN32 + STARTUPINFO start_info; + PROCESS_INFORMATION proc_info; + GetStartupInfo(&start_info); + if (!CreateProcess(nullptr, const_cast<char *>(proc.c_str()), nullptr, + nullptr, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, + nullptr, nullptr, &start_info, &proc_info)) + return; +# else + int pid = fork(); + if (pid != 0) { // The father process returns + } else { +# ifdef __linux__ + // Make sure the java process terminates on exit, since its + // broken socket detection seems to be useless. + prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0); +# endif + std::string mutable_args(args); + int argc = 1; + for (auto ch : mutable_args) { + if (ch == ' ') { + ++argc; + } + } + std::unique_ptr<char *[]> argv(new char *[argc + 2]); + std::string argv0(executable); + argv[0] = &argv0[0]; + argv[1] = &mutable_args[0]; + argc = 2; + bool inquote = false; + for (int i = 0; mutable_args[i]; ++i) { + if (!inquote && mutable_args[i] == ' ') { + mutable_args[i] = '\0'; + argv[argc++] = &mutable_args[i + 1]; + } else if (mutable_args[i] == '"') { + inquote = !inquote; + mutable_args[i] = ' '; + } + } + argv[argc] = nullptr; + execvp(executable, argv.get()); + } +# endif +} + +SVSemaphore::SVSemaphore() { +# ifdef _WIN32 + semaphore_ = CreateSemaphore(0, 0, 10, 0); +# elif defined(__APPLE__) + auto name = std::to_string(random()); + sem_unlink(name.c_str()); + semaphore_ = sem_open(name.c_str(), O_CREAT, S_IWUSR, 0); + if (semaphore_ == SEM_FAILED) { + perror("sem_open"); + } +# else + sem_init(&semaphore_, 0, 0); +# endif +} + +SVSemaphore::~SVSemaphore() { +# ifdef _WIN32 + CloseHandle(semaphore_); +# elif defined(__APPLE__) + sem_close(semaphore_); +# else + sem_close(&semaphore_); +# endif +} + +void SVSemaphore::Signal() { +# ifdef _WIN32 + ReleaseSemaphore(semaphore_, 1, nullptr); +# elif defined(__APPLE__) + sem_post(semaphore_); +# else + sem_post(&semaphore_); +# endif +} + +void SVSemaphore::Wait() { +# ifdef _WIN32 + WaitForSingleObject(semaphore_, INFINITE); +# elif defined(__APPLE__) + sem_wait(semaphore_); +# else + sem_wait(&semaphore_); +# endif +} + +// Place a message in the message buffer (and flush it). +void SVNetwork::Send(const char *msg) { + std::lock_guard<std::mutex> guard(mutex_send_); + msg_buffer_out_.append(msg); +} + +// Send the whole buffer. +void SVNetwork::Flush() { + std::lock_guard<std::mutex> guard(mutex_send_); + while (!msg_buffer_out_.empty()) { + int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0); + msg_buffer_out_.erase(0, i); + } +} + +// Receive a message from the server. +// This will always return one line of char* (denoted by \n). +char *SVNetwork::Receive() { + char *result = nullptr; + if (buffer_ptr_ != nullptr) { + result = strtok_r(nullptr, "\n", &buffer_ptr_); + } + + // This means there is something left in the buffer and we return it. + if (result != nullptr) { + return result; + // Otherwise, we read from the stream_. + } else { + buffer_ptr_ = nullptr; + + // The timeout length is not really important since we are looping anyway + // until a new message is delivered. + struct timeval tv; + tv.tv_sec = 10; + tv.tv_usec = 0; + + // Set the flags to return when the stream_ is ready to be read. + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(stream_, &readfds); + + int i = select(stream_ + 1, &readfds, nullptr, nullptr, &tv); + + // The stream_ died. + if (i == 0) { + return nullptr; + } + + // Read the message buffer. + i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0); + + // Server quit (0) or error (-1). + if (i <= 0) { + return nullptr; + } + msg_buffer_in_[i] = '\0'; + // Setup a new string tokenizer. + return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_); + } +} + +// Close the connection to the server. +void SVNetwork::Close() { +# ifdef _WIN32 + closesocket(stream_); +# else + close(stream_); +# endif + // Mark stream_ as invalid. + stream_ = -1; +} + +// The program to invoke to start ScrollView +static const char *ScrollViewProg() { +# ifdef _WIN32 + const char *prog = "java -Xms512m -Xmx1024m"; +# else + const char *prog = "sh"; +# endif + return prog; +} + +// The arguments to the program to invoke to start ScrollView +static std::string ScrollViewCommand(const std::string &scrollview_path) { + // Quote our paths on Windows to deal with spaces +# ifdef _WIN32 + const char cmd_template[] = + "-Djava.library.path=\"%s\" -jar \"%s/ScrollView.jar\""; +# else + const char cmd_template[] = + "-c \"trap 'kill %%1' 0 1 2 ; java " + "-Xms1024m -Xmx2048m -jar %s/ScrollView.jar" + " & wait\""; +# endif + size_t cmdlen = sizeof(cmd_template) + 2 * scrollview_path.size() + 1; + std::vector<char> cmd(cmdlen); + const char *sv_path = scrollview_path.c_str(); +# ifdef _WIN32 + snprintf(&cmd[0], cmdlen, cmd_template, sv_path, sv_path); +# else + snprintf(&cmd[0], cmdlen, cmd_template, sv_path); +# endif + std::string command(&cmd[0]); + return command; +} + +// Set up a connection to a ScrollView on hostname:port. +SVNetwork::SVNetwork(const char *hostname, int port) { + msg_buffer_in_ = new char[kMaxMsgSize + 1]; + msg_buffer_in_[0] = '\0'; + + buffer_ptr_ = nullptr; + + auto port_string = std::to_string(port); +# ifdef _WIN32 + // Initialize Winsock + WSADATA wsaData; + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + std::cerr << "WSAStartup failed: " << iResult << std::endl; + } +# endif // _WIN32 + + struct addrinfo *addr_info = nullptr; + struct addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(hostname, port_string.c_str(), &hints, &addr_info) != 0) { + std::cerr << "Error resolving name for ScrollView host " + << std::string(hostname) << ":" << port << std::endl; +# ifdef _WIN32 + WSACleanup(); +# endif // _WIN32 + } + + if (addr_info == nullptr) { + // Mark stream_ as invalid. + stream_ = -1; + } else { + stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, + addr_info->ai_protocol); + } + + if (stream_ < 0) { + std::cerr << "Failed to open socket" << std::endl; + } else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) { + // If server is not there, we will start a new server as local child + // process. + const char *scrollview_path = getenv("SCROLLVIEW_PATH"); + if (scrollview_path == nullptr) { +# ifdef SCROLLVIEW_PATH +# define _STR(a) # a +# define _XSTR(a) _STR(a) + scrollview_path = _XSTR(SCROLLVIEW_PATH); +# undef _XSTR +# undef _STR +# else + scrollview_path = "."; +# endif + } + const char *prog = ScrollViewProg(); + std::string command = ScrollViewCommand(scrollview_path); + SVSync::StartProcess(prog, command.c_str()); + + // Wait for server to show up. + // Note: There is no exception handling in case the server never turns up. + + Close(); + for (;;) { + stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, + addr_info->ai_protocol); + if (stream_ >= 0) { + if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) { + break; + } + + Close(); + + std::cout << "ScrollView: Waiting for server...\n"; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + } +# ifdef _WIN32 + // WSACleanup(); // This cause ScrollView windows is not displayed +# endif // _WIN32 + freeaddrinfo(addr_info); +} + +SVNetwork::~SVNetwork() { + Close(); + delete[] msg_buffer_in_; +} + +} // namespace tesseract + +#endif // !GRAPHICS_DISABLED
