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