Mercurial > hgrepos > Python2 > PyMuPDF
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /////////////////////////////////////////////////////////////////////// | |
| 2 // File: svutil.cpp | |
| 3 // Description: ScrollView Utilities | |
| 4 // Author: Joern Wanke | |
| 5 // | |
| 6 // (C) Copyright 2007, Google Inc. | |
| 7 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 8 // you may not use this file except in compliance with the License. | |
| 9 // You may obtain a copy of the License at | |
| 10 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 11 // Unless required by applicable law or agreed to in writing, software | |
| 12 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 // See the License for the specific language governing permissions and | |
| 15 // limitations under the License. | |
| 16 // | |
| 17 /////////////////////////////////////////////////////////////////////// | |
| 18 // | |
| 19 // SVUtil contains the SVSync and SVNetwork classes, which are used for | |
| 20 // thread/process creation & synchronization and network connection. | |
| 21 | |
| 22 // Include automatically generated configuration file if running autoconf. | |
| 23 #ifdef HAVE_CONFIG_H | |
| 24 # include "config_auto.h" | |
| 25 #endif | |
| 26 | |
| 27 #include "svutil.h" | |
| 28 | |
| 29 #include <cstdio> | |
| 30 #include <cstdlib> | |
| 31 #include <cstring> | |
| 32 #include <iostream> | |
| 33 #include <memory> | |
| 34 #include <string> | |
| 35 #include <thread> // for std::this_thread | |
| 36 #include <vector> | |
| 37 | |
| 38 #ifdef _WIN32 | |
| 39 # pragma comment(lib, "Ws2_32.lib") | |
| 40 # include <winsock2.h> // for fd_set, send, .. | |
| 41 # include <ws2tcpip.h> // for addrinfo | |
| 42 #else | |
| 43 # include <arpa/inet.h> | |
| 44 # include <netdb.h> | |
| 45 # include <netinet/in.h> | |
| 46 # include <semaphore.h> | |
| 47 # include <sys/select.h> | |
| 48 # include <sys/socket.h> | |
| 49 # include <csignal> | |
| 50 # ifdef __linux__ | |
| 51 # include <sys/prctl.h> | |
| 52 # endif | |
| 53 # include <unistd.h> | |
| 54 #endif | |
| 55 | |
| 56 #if defined(_WIN32) && !defined(__GNUC__) | |
| 57 # define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr) | |
| 58 #endif /* _WIN32 && !__GNUC__ */ | |
| 59 | |
| 60 #ifndef GRAPHICS_DISABLED | |
| 61 | |
| 62 namespace tesseract { | |
| 63 | |
| 64 const int kMaxMsgSize = 4096; | |
| 65 | |
| 66 // Starts a new process. | |
| 67 void SVSync::StartProcess(const char *executable, const char *args) { | |
| 68 std::string proc; | |
| 69 proc.append(executable); | |
| 70 proc.append(" "); | |
| 71 proc.append(args); | |
| 72 std::cout << "Starting " << proc << std::endl; | |
| 73 # ifdef _WIN32 | |
| 74 STARTUPINFO start_info; | |
| 75 PROCESS_INFORMATION proc_info; | |
| 76 GetStartupInfo(&start_info); | |
| 77 if (!CreateProcess(nullptr, const_cast<char *>(proc.c_str()), nullptr, | |
| 78 nullptr, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, | |
| 79 nullptr, nullptr, &start_info, &proc_info)) | |
| 80 return; | |
| 81 # else | |
| 82 int pid = fork(); | |
| 83 if (pid != 0) { // The father process returns | |
| 84 } else { | |
| 85 # ifdef __linux__ | |
| 86 // Make sure the java process terminates on exit, since its | |
| 87 // broken socket detection seems to be useless. | |
| 88 prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0); | |
| 89 # endif | |
| 90 std::string mutable_args(args); | |
| 91 int argc = 1; | |
| 92 for (auto ch : mutable_args) { | |
| 93 if (ch == ' ') { | |
| 94 ++argc; | |
| 95 } | |
| 96 } | |
| 97 std::unique_ptr<char *[]> argv(new char *[argc + 2]); | |
| 98 std::string argv0(executable); | |
| 99 argv[0] = &argv0[0]; | |
| 100 argv[1] = &mutable_args[0]; | |
| 101 argc = 2; | |
| 102 bool inquote = false; | |
| 103 for (int i = 0; mutable_args[i]; ++i) { | |
| 104 if (!inquote && mutable_args[i] == ' ') { | |
| 105 mutable_args[i] = '\0'; | |
| 106 argv[argc++] = &mutable_args[i + 1]; | |
| 107 } else if (mutable_args[i] == '"') { | |
| 108 inquote = !inquote; | |
| 109 mutable_args[i] = ' '; | |
| 110 } | |
| 111 } | |
| 112 argv[argc] = nullptr; | |
| 113 execvp(executable, argv.get()); | |
| 114 } | |
| 115 # endif | |
| 116 } | |
| 117 | |
| 118 SVSemaphore::SVSemaphore() { | |
| 119 # ifdef _WIN32 | |
| 120 semaphore_ = CreateSemaphore(0, 0, 10, 0); | |
| 121 # elif defined(__APPLE__) | |
| 122 auto name = std::to_string(random()); | |
| 123 sem_unlink(name.c_str()); | |
| 124 semaphore_ = sem_open(name.c_str(), O_CREAT, S_IWUSR, 0); | |
| 125 if (semaphore_ == SEM_FAILED) { | |
| 126 perror("sem_open"); | |
| 127 } | |
| 128 # else | |
| 129 sem_init(&semaphore_, 0, 0); | |
| 130 # endif | |
| 131 } | |
| 132 | |
| 133 SVSemaphore::~SVSemaphore() { | |
| 134 # ifdef _WIN32 | |
| 135 CloseHandle(semaphore_); | |
| 136 # elif defined(__APPLE__) | |
| 137 sem_close(semaphore_); | |
| 138 # else | |
| 139 sem_close(&semaphore_); | |
| 140 # endif | |
| 141 } | |
| 142 | |
| 143 void SVSemaphore::Signal() { | |
| 144 # ifdef _WIN32 | |
| 145 ReleaseSemaphore(semaphore_, 1, nullptr); | |
| 146 # elif defined(__APPLE__) | |
| 147 sem_post(semaphore_); | |
| 148 # else | |
| 149 sem_post(&semaphore_); | |
| 150 # endif | |
| 151 } | |
| 152 | |
| 153 void SVSemaphore::Wait() { | |
| 154 # ifdef _WIN32 | |
| 155 WaitForSingleObject(semaphore_, INFINITE); | |
| 156 # elif defined(__APPLE__) | |
| 157 sem_wait(semaphore_); | |
| 158 # else | |
| 159 sem_wait(&semaphore_); | |
| 160 # endif | |
| 161 } | |
| 162 | |
| 163 // Place a message in the message buffer (and flush it). | |
| 164 void SVNetwork::Send(const char *msg) { | |
| 165 std::lock_guard<std::mutex> guard(mutex_send_); | |
| 166 msg_buffer_out_.append(msg); | |
| 167 } | |
| 168 | |
| 169 // Send the whole buffer. | |
| 170 void SVNetwork::Flush() { | |
| 171 std::lock_guard<std::mutex> guard(mutex_send_); | |
| 172 while (!msg_buffer_out_.empty()) { | |
| 173 int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0); | |
| 174 msg_buffer_out_.erase(0, i); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 // Receive a message from the server. | |
| 179 // This will always return one line of char* (denoted by \n). | |
| 180 char *SVNetwork::Receive() { | |
| 181 char *result = nullptr; | |
| 182 if (buffer_ptr_ != nullptr) { | |
| 183 result = strtok_r(nullptr, "\n", &buffer_ptr_); | |
| 184 } | |
| 185 | |
| 186 // This means there is something left in the buffer and we return it. | |
| 187 if (result != nullptr) { | |
| 188 return result; | |
| 189 // Otherwise, we read from the stream_. | |
| 190 } else { | |
| 191 buffer_ptr_ = nullptr; | |
| 192 | |
| 193 // The timeout length is not really important since we are looping anyway | |
| 194 // until a new message is delivered. | |
| 195 struct timeval tv; | |
| 196 tv.tv_sec = 10; | |
| 197 tv.tv_usec = 0; | |
| 198 | |
| 199 // Set the flags to return when the stream_ is ready to be read. | |
| 200 fd_set readfds; | |
| 201 FD_ZERO(&readfds); | |
| 202 FD_SET(stream_, &readfds); | |
| 203 | |
| 204 int i = select(stream_ + 1, &readfds, nullptr, nullptr, &tv); | |
| 205 | |
| 206 // The stream_ died. | |
| 207 if (i == 0) { | |
| 208 return nullptr; | |
| 209 } | |
| 210 | |
| 211 // Read the message buffer. | |
| 212 i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0); | |
| 213 | |
| 214 // Server quit (0) or error (-1). | |
| 215 if (i <= 0) { | |
| 216 return nullptr; | |
| 217 } | |
| 218 msg_buffer_in_[i] = '\0'; | |
| 219 // Setup a new string tokenizer. | |
| 220 return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 // Close the connection to the server. | |
| 225 void SVNetwork::Close() { | |
| 226 # ifdef _WIN32 | |
| 227 closesocket(stream_); | |
| 228 # else | |
| 229 close(stream_); | |
| 230 # endif | |
| 231 // Mark stream_ as invalid. | |
| 232 stream_ = -1; | |
| 233 } | |
| 234 | |
| 235 // The program to invoke to start ScrollView | |
| 236 static const char *ScrollViewProg() { | |
| 237 # ifdef _WIN32 | |
| 238 const char *prog = "java -Xms512m -Xmx1024m"; | |
| 239 # else | |
| 240 const char *prog = "sh"; | |
| 241 # endif | |
| 242 return prog; | |
| 243 } | |
| 244 | |
| 245 // The arguments to the program to invoke to start ScrollView | |
| 246 static std::string ScrollViewCommand(const std::string &scrollview_path) { | |
| 247 // Quote our paths on Windows to deal with spaces | |
| 248 # ifdef _WIN32 | |
| 249 const char cmd_template[] = | |
| 250 "-Djava.library.path=\"%s\" -jar \"%s/ScrollView.jar\""; | |
| 251 # else | |
| 252 const char cmd_template[] = | |
| 253 "-c \"trap 'kill %%1' 0 1 2 ; java " | |
| 254 "-Xms1024m -Xmx2048m -jar %s/ScrollView.jar" | |
| 255 " & wait\""; | |
| 256 # endif | |
| 257 size_t cmdlen = sizeof(cmd_template) + 2 * scrollview_path.size() + 1; | |
| 258 std::vector<char> cmd(cmdlen); | |
| 259 const char *sv_path = scrollview_path.c_str(); | |
| 260 # ifdef _WIN32 | |
| 261 snprintf(&cmd[0], cmdlen, cmd_template, sv_path, sv_path); | |
| 262 # else | |
| 263 snprintf(&cmd[0], cmdlen, cmd_template, sv_path); | |
| 264 # endif | |
| 265 std::string command(&cmd[0]); | |
| 266 return command; | |
| 267 } | |
| 268 | |
| 269 // Set up a connection to a ScrollView on hostname:port. | |
| 270 SVNetwork::SVNetwork(const char *hostname, int port) { | |
| 271 msg_buffer_in_ = new char[kMaxMsgSize + 1]; | |
| 272 msg_buffer_in_[0] = '\0'; | |
| 273 | |
| 274 buffer_ptr_ = nullptr; | |
| 275 | |
| 276 auto port_string = std::to_string(port); | |
| 277 # ifdef _WIN32 | |
| 278 // Initialize Winsock | |
| 279 WSADATA wsaData; | |
| 280 int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); | |
| 281 if (iResult != 0) { | |
| 282 std::cerr << "WSAStartup failed: " << iResult << std::endl; | |
| 283 } | |
| 284 # endif // _WIN32 | |
| 285 | |
| 286 struct addrinfo *addr_info = nullptr; | |
| 287 struct addrinfo hints = {}; | |
| 288 hints.ai_family = AF_INET; | |
| 289 hints.ai_socktype = SOCK_STREAM; | |
| 290 if (getaddrinfo(hostname, port_string.c_str(), &hints, &addr_info) != 0) { | |
| 291 std::cerr << "Error resolving name for ScrollView host " | |
| 292 << std::string(hostname) << ":" << port << std::endl; | |
| 293 # ifdef _WIN32 | |
| 294 WSACleanup(); | |
| 295 # endif // _WIN32 | |
| 296 } | |
| 297 | |
| 298 if (addr_info == nullptr) { | |
| 299 // Mark stream_ as invalid. | |
| 300 stream_ = -1; | |
| 301 } else { | |
| 302 stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, | |
| 303 addr_info->ai_protocol); | |
| 304 } | |
| 305 | |
| 306 if (stream_ < 0) { | |
| 307 std::cerr << "Failed to open socket" << std::endl; | |
| 308 } else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) { | |
| 309 // If server is not there, we will start a new server as local child | |
| 310 // process. | |
| 311 const char *scrollview_path = getenv("SCROLLVIEW_PATH"); | |
| 312 if (scrollview_path == nullptr) { | |
| 313 # ifdef SCROLLVIEW_PATH | |
| 314 # define _STR(a) # a | |
| 315 # define _XSTR(a) _STR(a) | |
| 316 scrollview_path = _XSTR(SCROLLVIEW_PATH); | |
| 317 # undef _XSTR | |
| 318 # undef _STR | |
| 319 # else | |
| 320 scrollview_path = "."; | |
| 321 # endif | |
| 322 } | |
| 323 const char *prog = ScrollViewProg(); | |
| 324 std::string command = ScrollViewCommand(scrollview_path); | |
| 325 SVSync::StartProcess(prog, command.c_str()); | |
| 326 | |
| 327 // Wait for server to show up. | |
| 328 // Note: There is no exception handling in case the server never turns up. | |
| 329 | |
| 330 Close(); | |
| 331 for (;;) { | |
| 332 stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, | |
| 333 addr_info->ai_protocol); | |
| 334 if (stream_ >= 0) { | |
| 335 if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) { | |
| 336 break; | |
| 337 } | |
| 338 | |
| 339 Close(); | |
| 340 | |
| 341 std::cout << "ScrollView: Waiting for server...\n"; | |
| 342 std::this_thread::sleep_for(std::chrono::seconds(1)); | |
| 343 } | |
| 344 } | |
| 345 } | |
| 346 # ifdef _WIN32 | |
| 347 // WSACleanup(); // This cause ScrollView windows is not displayed | |
| 348 # endif // _WIN32 | |
| 349 freeaddrinfo(addr_info); | |
| 350 } | |
| 351 | |
| 352 SVNetwork::~SVNetwork() { | |
| 353 Close(); | |
| 354 delete[] msg_buffer_in_; | |
| 355 } | |
| 356 | |
| 357 } // namespace tesseract | |
| 358 | |
| 359 #endif // !GRAPHICS_DISABLED |
