Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/tesseract/src/viewer/scrollview.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: scrollview.cpp | |
| 3 // Description: ScrollView | |
| 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 | |
| 20 // Include automatically generated configuration file if running autoconf. | |
| 21 #ifdef HAVE_CONFIG_H | |
| 22 # include "config_auto.h" | |
| 23 #endif | |
| 24 | |
| 25 #include "scrollview.h" | |
| 26 | |
| 27 #include "svutil.h" // for SVNetwork | |
| 28 | |
| 29 #include <allheaders.h> | |
| 30 | |
| 31 #include <algorithm> | |
| 32 #include <climits> | |
| 33 #include <cstdarg> | |
| 34 #include <cstring> | |
| 35 #include <map> | |
| 36 #include <memory> // for std::unique_ptr | |
| 37 #include <mutex> // for std::mutex | |
| 38 #include <string> | |
| 39 #include <thread> // for std::thread | |
| 40 #include <utility> | |
| 41 #include <vector> | |
| 42 | |
| 43 namespace tesseract { | |
| 44 | |
| 45 const int kSvPort = 8461; | |
| 46 const int kMaxMsgSize = 4096; | |
| 47 const int kMaxIntPairSize = 45; // Holds %d,%d, for up to 64 bit. | |
| 48 | |
| 49 struct SVPolyLineBuffer { | |
| 50 bool empty; // Independent indicator to allow SendMsg to call SendPolygon. | |
| 51 std::vector<int> xcoords; | |
| 52 std::vector<int> ycoords; | |
| 53 }; | |
| 54 | |
| 55 // A map between the window IDs and their corresponding pointers. | |
| 56 static std::map<int, ScrollView *> svmap; | |
| 57 static std::mutex *svmap_mu; | |
| 58 // A map of all semaphores waiting for a specific event on a specific window. | |
| 59 static std::map<std::pair<ScrollView *, SVEventType>, | |
| 60 std::pair<SVSemaphore *, std::unique_ptr<SVEvent>>> waiting_for_events; | |
| 61 static std::mutex *waiting_for_events_mu; | |
| 62 | |
| 63 std::unique_ptr<SVEvent> SVEvent::copy() const { | |
| 64 auto any = std::unique_ptr<SVEvent>(new SVEvent); | |
| 65 any->command_id = command_id; | |
| 66 any->counter = counter; | |
| 67 any->parameter = new char[strlen(parameter) + 1]; | |
| 68 strcpy(any->parameter, parameter); | |
| 69 any->type = type; | |
| 70 any->x = x; | |
| 71 any->y = y; | |
| 72 any->x_size = x_size; | |
| 73 any->y_size = y_size; | |
| 74 any->window = window; | |
| 75 return any; | |
| 76 } | |
| 77 | |
| 78 // Destructor. | |
| 79 // It is defined here, so the compiler can create a single vtable | |
| 80 // instead of weak vtables in every compilation unit. | |
| 81 SVEventHandler::~SVEventHandler() = default; | |
| 82 | |
| 83 #ifndef GRAPHICS_DISABLED | |
| 84 /// This is the main loop which handles the ScrollView-logic from the server | |
| 85 /// to the client. It basically loops through messages, parses them to events | |
| 86 /// and distributes it to the waiting handlers. | |
| 87 /// It is run from a different thread and synchronizes via SVSync. | |
| 88 void ScrollView::MessageReceiver() { | |
| 89 int counter_event_id = 0; // ongoing counter | |
| 90 char *message = nullptr; | |
| 91 // Wait until a new message appears in the input stream_. | |
| 92 do { | |
| 93 message = ScrollView::GetStream()->Receive(); | |
| 94 } while (message == nullptr); | |
| 95 | |
| 96 // This is the main loop which iterates until the server is dead (strlen = | |
| 97 // -1). It basically parses for 3 different messagetypes and then distributes | |
| 98 // the events accordingly. | |
| 99 while (true) { | |
| 100 // The new event we create. | |
| 101 std::unique_ptr<SVEvent> cur(new SVEvent); | |
| 102 // The ID of the corresponding window. | |
| 103 int window_id; | |
| 104 | |
| 105 int ev_type; | |
| 106 | |
| 107 int n; | |
| 108 // Fill the new SVEvent properly. | |
| 109 sscanf(message, "%d,%d,%d,%d,%d,%d,%d,%n", &window_id, &ev_type, &cur->x, &cur->y, &cur->x_size, | |
| 110 &cur->y_size, &cur->command_id, &n); | |
| 111 char *p = (message + n); | |
| 112 | |
| 113 svmap_mu->lock(); | |
| 114 cur->window = svmap[window_id]; | |
| 115 | |
| 116 if (cur->window != nullptr) { | |
| 117 auto length = strlen(p); | |
| 118 cur->parameter = new char[length + 1]; | |
| 119 strcpy(cur->parameter, p); | |
| 120 if (length > 0) { // remove the last \n | |
| 121 cur->parameter[length - 1] = '\0'; | |
| 122 } | |
| 123 cur->type = static_cast<SVEventType>(ev_type); | |
| 124 // Correct selection coordinates so x,y is the min pt and size is +ve. | |
| 125 if (cur->x_size > 0) { | |
| 126 cur->x -= cur->x_size; | |
| 127 } else { | |
| 128 cur->x_size = -cur->x_size; | |
| 129 } | |
| 130 if (cur->y_size > 0) { | |
| 131 cur->y -= cur->y_size; | |
| 132 } else { | |
| 133 cur->y_size = -cur->y_size; | |
| 134 } | |
| 135 // Returned y will be the bottom-left if y is reversed. | |
| 136 if (cur->window->y_axis_is_reversed_) { | |
| 137 cur->y = cur->window->TranslateYCoordinate(cur->y + cur->y_size); | |
| 138 } | |
| 139 cur->counter = counter_event_id; | |
| 140 // Increase by 2 since we will also create an SVET_ANY event from cur, | |
| 141 // which will have a counter_id of cur + 1 (and thus gets processed | |
| 142 // after cur). | |
| 143 counter_event_id += 2; | |
| 144 | |
| 145 // In case of an SVET_EXIT event, quit the whole application. | |
| 146 if (ev_type == SVET_EXIT) { | |
| 147 SendRawMessage("svmain:exit()"); | |
| 148 break; | |
| 149 } | |
| 150 | |
| 151 // Place two copies of it in the table for the window. | |
| 152 cur->window->SetEvent(cur.get()); | |
| 153 | |
| 154 // Check if any of the threads currently waiting want it. | |
| 155 std::pair<ScrollView *, SVEventType> awaiting_list(cur->window, cur->type); | |
| 156 std::pair<ScrollView *, SVEventType> awaiting_list_any(cur->window, SVET_ANY); | |
| 157 std::pair<ScrollView *, SVEventType> awaiting_list_any_window((ScrollView *)nullptr, | |
| 158 SVET_ANY); | |
| 159 waiting_for_events_mu->lock(); | |
| 160 if (waiting_for_events.count(awaiting_list) > 0) { | |
| 161 waiting_for_events[awaiting_list].second = std::move(cur); | |
| 162 waiting_for_events[awaiting_list].first->Signal(); | |
| 163 } else if (waiting_for_events.count(awaiting_list_any) > 0) { | |
| 164 waiting_for_events[awaiting_list_any].second = std::move(cur); | |
| 165 waiting_for_events[awaiting_list_any].first->Signal(); | |
| 166 } else if (waiting_for_events.count(awaiting_list_any_window) > 0) { | |
| 167 waiting_for_events[awaiting_list_any_window].second = std::move(cur); | |
| 168 waiting_for_events[awaiting_list_any_window].first->Signal(); | |
| 169 } | |
| 170 waiting_for_events_mu->unlock(); | |
| 171 // Signal the corresponding semaphore twice (for both copies). | |
| 172 ScrollView *sv = svmap[window_id]; | |
| 173 if (sv != nullptr) { | |
| 174 sv->Signal(); | |
| 175 sv->Signal(); | |
| 176 } | |
| 177 } | |
| 178 svmap_mu->unlock(); | |
| 179 | |
| 180 // Wait until a new message appears in the input stream_. | |
| 181 do { | |
| 182 message = ScrollView::GetStream()->Receive(); | |
| 183 } while (message == nullptr); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 // Table to implement the color index values in the old system. | |
| 188 static const uint8_t table_colors[ScrollView::GREEN_YELLOW + 1][4] = { | |
| 189 {0, 0, 0, 0}, // NONE (transparent) | |
| 190 {0, 0, 0, 255}, // BLACK. | |
| 191 {255, 255, 255, 255}, // WHITE. | |
| 192 {255, 0, 0, 255}, // RED. | |
| 193 {255, 255, 0, 255}, // YELLOW. | |
| 194 {0, 255, 0, 255}, // GREEN. | |
| 195 {0, 255, 255, 255}, // CYAN. | |
| 196 {0, 0, 255, 255}, // BLUE. | |
| 197 {255, 0, 255, 255}, // MAGENTA. | |
| 198 {0, 128, 255, 255}, // AQUAMARINE. | |
| 199 {0, 0, 64, 255}, // DARK_SLATE_BLUE. | |
| 200 {128, 128, 255, 255}, // LIGHT_BLUE. | |
| 201 {64, 64, 255, 255}, // MEDIUM_BLUE. | |
| 202 {0, 0, 32, 255}, // MIDNIGHT_BLUE. | |
| 203 {0, 0, 128, 255}, // NAVY_BLUE. | |
| 204 {192, 192, 255, 255}, // SKY_BLUE. | |
| 205 {64, 64, 128, 255}, // SLATE_BLUE. | |
| 206 {32, 32, 64, 255}, // STEEL_BLUE. | |
| 207 {255, 128, 128, 255}, // CORAL. | |
| 208 {128, 64, 0, 255}, // BROWN. | |
| 209 {128, 128, 0, 255}, // SANDY_BROWN. | |
| 210 {192, 192, 0, 255}, // GOLD. | |
| 211 {192, 192, 128, 255}, // GOLDENROD. | |
| 212 {0, 64, 0, 255}, // DARK_GREEN. | |
| 213 {32, 64, 0, 255}, // DARK_OLIVE_GREEN. | |
| 214 {64, 128, 0, 255}, // FOREST_GREEN. | |
| 215 {128, 255, 0, 255}, // LIME_GREEN. | |
| 216 {192, 255, 192, 255}, // PALE_GREEN. | |
| 217 {192, 255, 0, 255}, // YELLOW_GREEN. | |
| 218 {192, 192, 192, 255}, // LIGHT_GREY. | |
| 219 {64, 64, 128, 255}, // DARK_SLATE_GREY. | |
| 220 {64, 64, 64, 255}, // DIM_GREY. | |
| 221 {128, 128, 128, 255}, // GREY. | |
| 222 {64, 192, 0, 255}, // KHAKI. | |
| 223 {255, 0, 192, 255}, // MAROON. | |
| 224 {255, 128, 0, 255}, // ORANGE. | |
| 225 {255, 128, 64, 255}, // ORCHID. | |
| 226 {255, 192, 192, 255}, // PINK. | |
| 227 {128, 0, 128, 255}, // PLUM. | |
| 228 {255, 0, 64, 255}, // INDIAN_RED. | |
| 229 {255, 64, 0, 255}, // ORANGE_RED. | |
| 230 {255, 0, 192, 255}, // VIOLET_RED. | |
| 231 {255, 192, 128, 255}, // SALMON. | |
| 232 {128, 128, 0, 255}, // TAN. | |
| 233 {0, 255, 255, 255}, // TURQUOISE. | |
| 234 {0, 128, 128, 255}, // DARK_TURQUOISE. | |
| 235 {192, 0, 255, 255}, // VIOLET. | |
| 236 {128, 128, 0, 255}, // WHEAT. | |
| 237 {128, 255, 0, 255} // GREEN_YELLOW | |
| 238 }; | |
| 239 | |
| 240 /******************************************************************************* | |
| 241 * Scrollview implementation. | |
| 242 *******************************************************************************/ | |
| 243 | |
| 244 SVNetwork *ScrollView::stream_ = nullptr; | |
| 245 int ScrollView::nr_created_windows_ = 0; | |
| 246 int ScrollView::image_index_ = 0; | |
| 247 | |
| 248 /// Calls Initialize with all arguments given. | |
| 249 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size, | |
| 250 int x_canvas_size, int y_canvas_size, bool y_axis_reversed, | |
| 251 const char *server_name) { | |
| 252 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, y_axis_reversed, | |
| 253 server_name); | |
| 254 } | |
| 255 | |
| 256 /// Calls Initialize with default argument for server_name_. | |
| 257 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size, | |
| 258 int x_canvas_size, int y_canvas_size, bool y_axis_reversed) { | |
| 259 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, y_axis_reversed, | |
| 260 "localhost"); | |
| 261 } | |
| 262 | |
| 263 /// Calls Initialize with default argument for server_name_ & y_axis_reversed. | |
| 264 ScrollView::ScrollView(const char *name, int x_pos, int y_pos, int x_size, int y_size, | |
| 265 int x_canvas_size, int y_canvas_size) { | |
| 266 Initialize(name, x_pos, y_pos, x_size, y_size, x_canvas_size, y_canvas_size, false, "localhost"); | |
| 267 } | |
| 268 | |
| 269 /// Sets up a ScrollView window, depending on the constructor variables. | |
| 270 void ScrollView::Initialize(const char *name, int x_pos, int y_pos, int x_size, int y_size, | |
| 271 int x_canvas_size, int y_canvas_size, bool y_axis_reversed, | |
| 272 const char *server_name) { | |
| 273 // If this is the first ScrollView Window which gets created, there is no | |
| 274 // network connection yet and we have to set it up in a different thread. | |
| 275 if (stream_ == nullptr) { | |
| 276 nr_created_windows_ = 0; | |
| 277 stream_ = new SVNetwork(server_name, kSvPort); | |
| 278 waiting_for_events_mu = new std::mutex(); | |
| 279 svmap_mu = new std::mutex(); | |
| 280 SendRawMessage("svmain = luajava.bindClass('com.google.scrollview.ScrollView')\n"); | |
| 281 std::thread t(&ScrollView::MessageReceiver); | |
| 282 t.detach(); | |
| 283 } | |
| 284 | |
| 285 // Set up the variables on the clientside. | |
| 286 nr_created_windows_++; | |
| 287 event_handler_ = nullptr; | |
| 288 event_handler_ended_ = false; | |
| 289 y_axis_is_reversed_ = y_axis_reversed; | |
| 290 y_size_ = y_canvas_size; | |
| 291 window_name_ = name; | |
| 292 window_id_ = nr_created_windows_; | |
| 293 // Set up polygon buffering. | |
| 294 points_ = new SVPolyLineBuffer; | |
| 295 points_->empty = true; | |
| 296 | |
| 297 svmap_mu->lock(); | |
| 298 svmap[window_id_] = this; | |
| 299 svmap_mu->unlock(); | |
| 300 | |
| 301 for (auto &i : event_table_) { | |
| 302 i = nullptr; | |
| 303 } | |
| 304 | |
| 305 semaphore_ = new SVSemaphore(); | |
| 306 | |
| 307 // Set up an actual Window on the client side. | |
| 308 char message[kMaxMsgSize]; | |
| 309 snprintf(message, sizeof(message), | |
| 310 "w%d = luajava.newInstance('com.google.scrollview.ui" | |
| 311 ".SVWindow','%s',%u,%u,%u,%u,%u,%u,%u)\n", | |
| 312 window_id_, window_name_, window_id_, x_pos, y_pos, x_size, y_size, x_canvas_size, | |
| 313 y_canvas_size); | |
| 314 SendRawMessage(message); | |
| 315 | |
| 316 std::thread t(&ScrollView::StartEventHandler, this); | |
| 317 t.detach(); | |
| 318 } | |
| 319 | |
| 320 /// Sits and waits for events on this window. | |
| 321 void ScrollView::StartEventHandler() { | |
| 322 for (;;) { | |
| 323 stream_->Flush(); | |
| 324 semaphore_->Wait(); | |
| 325 int serial = -1; | |
| 326 int k = -1; | |
| 327 mutex_.lock(); | |
| 328 // Check every table entry if it is valid and not already processed. | |
| 329 | |
| 330 for (int i = 0; i < SVET_COUNT; i++) { | |
| 331 if (event_table_[i] != nullptr && (serial < 0 || event_table_[i]->counter < serial)) { | |
| 332 serial = event_table_[i]->counter; | |
| 333 k = i; | |
| 334 } | |
| 335 } | |
| 336 // If we didn't find anything we had an old alarm and just sleep again. | |
| 337 if (k != -1) { | |
| 338 auto new_event = std::move(event_table_[k]); | |
| 339 mutex_.unlock(); | |
| 340 if (event_handler_ != nullptr) { | |
| 341 event_handler_->Notify(new_event.get()); | |
| 342 } | |
| 343 if (new_event->type == SVET_DESTROY) { | |
| 344 // Signal the destructor that it is safe to terminate. | |
| 345 event_handler_ended_ = true; | |
| 346 return; | |
| 347 } | |
| 348 } else { | |
| 349 mutex_.unlock(); | |
| 350 } | |
| 351 // The thread should run as long as its associated window is alive. | |
| 352 } | |
| 353 } | |
| 354 #endif // !GRAPHICS_DISABLED | |
| 355 | |
| 356 ScrollView::~ScrollView() { | |
| 357 #ifndef GRAPHICS_DISABLED | |
| 358 svmap_mu->lock(); | |
| 359 if (svmap[window_id_] != nullptr) { | |
| 360 svmap_mu->unlock(); | |
| 361 // So the event handling thread can quit. | |
| 362 SendMsg("destroy()"); | |
| 363 | |
| 364 AwaitEvent(SVET_DESTROY); | |
| 365 svmap_mu->lock(); | |
| 366 svmap[window_id_] = nullptr; | |
| 367 svmap_mu->unlock(); | |
| 368 // The event handler thread for this window *must* receive the | |
| 369 // destroy event and set its pointer to this to nullptr before we allow | |
| 370 // the destructor to exit. | |
| 371 while (!event_handler_ended_) { | |
| 372 Update(); | |
| 373 } | |
| 374 } else { | |
| 375 svmap_mu->unlock(); | |
| 376 } | |
| 377 delete semaphore_; | |
| 378 delete points_; | |
| 379 #endif // !GRAPHICS_DISABLED | |
| 380 } | |
| 381 | |
| 382 #ifndef GRAPHICS_DISABLED | |
| 383 /// Send a message to the server, attaching the window id. | |
| 384 void ScrollView::SendMsg(const char *format, ...) { | |
| 385 if (!points_->empty) { | |
| 386 SendPolygon(); | |
| 387 } | |
| 388 va_list args; | |
| 389 char message[kMaxMsgSize - 4]; | |
| 390 | |
| 391 va_start(args, format); // variable list | |
| 392 vsnprintf(message, sizeof(message), format, args); | |
| 393 va_end(args); | |
| 394 | |
| 395 char form[kMaxMsgSize]; | |
| 396 snprintf(form, sizeof(form), "w%d:%s\n", window_id_, message); | |
| 397 | |
| 398 stream_->Send(form); | |
| 399 } | |
| 400 | |
| 401 /// Send a message to the server without a | |
| 402 /// window id. Used for global events like exit(). | |
| 403 void ScrollView::SendRawMessage(const char *msg) { | |
| 404 stream_->Send(msg); | |
| 405 } | |
| 406 | |
| 407 /// Add an Event Listener to this ScrollView Window | |
| 408 void ScrollView::AddEventHandler(SVEventHandler *listener) { | |
| 409 event_handler_ = listener; | |
| 410 } | |
| 411 | |
| 412 void ScrollView::Signal() { | |
| 413 semaphore_->Signal(); | |
| 414 } | |
| 415 | |
| 416 void ScrollView::SetEvent(const SVEvent *svevent) { | |
| 417 // Copy event | |
| 418 auto any = svevent->copy(); | |
| 419 auto specific = svevent->copy(); | |
| 420 any->counter = specific->counter + 1; | |
| 421 | |
| 422 // Place both events into the queue. | |
| 423 std::lock_guard<std::mutex> guard(mutex_); | |
| 424 | |
| 425 event_table_[specific->type] = std::move(specific); | |
| 426 event_table_[SVET_ANY] = std::move(any); | |
| 427 } | |
| 428 | |
| 429 /// Block until an event of the given type is received. | |
| 430 /// Note: The calling function is responsible for deleting the returned | |
| 431 /// SVEvent afterwards! | |
| 432 std::unique_ptr<SVEvent> ScrollView::AwaitEvent(SVEventType type) { | |
| 433 // Initialize the waiting semaphore. | |
| 434 auto *sem = new SVSemaphore(); | |
| 435 std::pair<ScrollView *, SVEventType> ea(this, type); | |
| 436 waiting_for_events_mu->lock(); | |
| 437 waiting_for_events[ea] = {sem, nullptr}; | |
| 438 waiting_for_events_mu->unlock(); | |
| 439 // Wait on it, but first flush. | |
| 440 stream_->Flush(); | |
| 441 sem->Wait(); | |
| 442 // Process the event we got woken up for (its in waiting_for_events pair). | |
| 443 waiting_for_events_mu->lock(); | |
| 444 auto ret = std::move(waiting_for_events[ea].second); | |
| 445 waiting_for_events.erase(ea); | |
| 446 delete sem; | |
| 447 waiting_for_events_mu->unlock(); | |
| 448 return ret; | |
| 449 } | |
| 450 | |
| 451 // Send the current buffered polygon (if any) and clear it. | |
| 452 void ScrollView::SendPolygon() { | |
| 453 if (!points_->empty) { | |
| 454 points_->empty = true; // Allows us to use SendMsg. | |
| 455 int length = points_->xcoords.size(); | |
| 456 // length == 1 corresponds to 2 SetCursors in a row and only the | |
| 457 // last setCursor has any effect. | |
| 458 if (length == 2) { | |
| 459 // An isolated line! | |
| 460 SendMsg("drawLine(%d,%d,%d,%d)", points_->xcoords[0], points_->ycoords[0], | |
| 461 points_->xcoords[1], points_->ycoords[1]); | |
| 462 } else if (length > 2) { | |
| 463 // A polyline. | |
| 464 SendMsg("createPolyline(%d)", length); | |
| 465 char coordpair[kMaxIntPairSize]; | |
| 466 std::string decimal_coords; | |
| 467 for (int i = 0; i < length; ++i) { | |
| 468 snprintf(coordpair, kMaxIntPairSize, "%d,%d,", points_->xcoords[i], points_->ycoords[i]); | |
| 469 decimal_coords += coordpair; | |
| 470 } | |
| 471 decimal_coords += '\n'; | |
| 472 SendRawMessage(decimal_coords.c_str()); | |
| 473 SendMsg("drawPolyline()"); | |
| 474 } | |
| 475 points_->xcoords.clear(); | |
| 476 points_->ycoords.clear(); | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 /******************************************************************************* | |
| 481 * LUA "API" functions. | |
| 482 *******************************************************************************/ | |
| 483 | |
| 484 // Sets the position from which to draw to (x,y). | |
| 485 void ScrollView::SetCursor(int x, int y) { | |
| 486 SendPolygon(); | |
| 487 DrawTo(x, y); | |
| 488 } | |
| 489 | |
| 490 // Draws from the current position to (x,y) and sets the new position to it. | |
| 491 void ScrollView::DrawTo(int x, int y) { | |
| 492 points_->xcoords.push_back(x); | |
| 493 points_->ycoords.push_back(TranslateYCoordinate(y)); | |
| 494 points_->empty = false; | |
| 495 } | |
| 496 | |
| 497 // Draw a line using the current pen color. | |
| 498 void ScrollView::Line(int x1, int y1, int x2, int y2) { | |
| 499 if (!points_->xcoords.empty() && x1 == points_->xcoords.back() && | |
| 500 TranslateYCoordinate(y1) == points_->ycoords.back()) { | |
| 501 // We are already at x1, y1, so just draw to x2, y2. | |
| 502 DrawTo(x2, y2); | |
| 503 } else if (!points_->xcoords.empty() && x2 == points_->xcoords.back() && | |
| 504 TranslateYCoordinate(y2) == points_->ycoords.back()) { | |
| 505 // We are already at x2, y2, so just draw to x1, y1. | |
| 506 DrawTo(x1, y1); | |
| 507 } else { | |
| 508 // This is a new line. | |
| 509 SetCursor(x1, y1); | |
| 510 DrawTo(x2, y2); | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 // Set the visibility of the window. | |
| 515 void ScrollView::SetVisible(bool visible) { | |
| 516 if (visible) { | |
| 517 SendMsg("setVisible(true)"); | |
| 518 } else { | |
| 519 SendMsg("setVisible(false)"); | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 // Set the alwaysOnTop flag. | |
| 524 void ScrollView::AlwaysOnTop(bool b) { | |
| 525 if (b) { | |
| 526 SendMsg("setAlwaysOnTop(true)"); | |
| 527 } else { | |
| 528 SendMsg("setAlwaysOnTop(false)"); | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 // Adds a message entry to the message box. | |
| 533 void ScrollView::AddMessage(const char *message) { | |
| 534 char form[kMaxMsgSize]; | |
| 535 snprintf(form, sizeof(form), "w%d:%s", window_id_, message); | |
| 536 | |
| 537 char *esc = AddEscapeChars(form); | |
| 538 SendMsg("addMessage(\"%s\")", esc); | |
| 539 delete[] esc; | |
| 540 } | |
| 541 | |
| 542 void ScrollView::AddMessageF(const char *format, ...) { | |
| 543 va_list args; | |
| 544 char message[kMaxMsgSize - 4]; | |
| 545 | |
| 546 va_start(args, format); // variable list | |
| 547 vsnprintf(message, sizeof(message), format, args); | |
| 548 va_end(args); | |
| 549 | |
| 550 AddMessage(message); | |
| 551 } | |
| 552 | |
| 553 // Set a messagebox. | |
| 554 void ScrollView::AddMessageBox() { | |
| 555 SendMsg("addMessageBox()"); | |
| 556 } | |
| 557 | |
| 558 // Exit the client completely (and notify the server of it). | |
| 559 void ScrollView::Exit() { | |
| 560 SendRawMessage("svmain:exit()"); | |
| 561 exit(0); | |
| 562 } | |
| 563 | |
| 564 // Clear the canvas. | |
| 565 void ScrollView::Clear() { | |
| 566 SendMsg("clear()"); | |
| 567 } | |
| 568 | |
| 569 // Set the stroke width. | |
| 570 void ScrollView::Stroke(float width) { | |
| 571 SendMsg("setStrokeWidth(%f)", width); | |
| 572 } | |
| 573 | |
| 574 // Draw a rectangle using the current pen color. | |
| 575 // The rectangle is filled with the current brush color. | |
| 576 void ScrollView::Rectangle(int x1, int y1, int x2, int y2) { | |
| 577 if (x1 == x2 && y1 == y2) { | |
| 578 return; // Scrollviewer locks up. | |
| 579 } | |
| 580 SendMsg("drawRectangle(%d,%d,%d,%d)", x1, TranslateYCoordinate(y1), x2, TranslateYCoordinate(y2)); | |
| 581 } | |
| 582 | |
| 583 // Draw an ellipse using the current pen color. | |
| 584 // The ellipse is filled with the current brush color. | |
| 585 void ScrollView::Ellipse(int x1, int y1, int width, int height) { | |
| 586 SendMsg("drawEllipse(%d,%d,%u,%u)", x1, TranslateYCoordinate(y1), width, height); | |
| 587 } | |
| 588 | |
| 589 // Set the pen color to the given RGB values. | |
| 590 void ScrollView::Pen(int red, int green, int blue) { | |
| 591 SendMsg("pen(%d,%d,%d)", red, green, blue); | |
| 592 } | |
| 593 | |
| 594 // Set the pen color to the given RGB values. | |
| 595 void ScrollView::Pen(int red, int green, int blue, int alpha) { | |
| 596 SendMsg("pen(%d,%d,%d,%d)", red, green, blue, alpha); | |
| 597 } | |
| 598 | |
| 599 // Set the brush color to the given RGB values. | |
| 600 void ScrollView::Brush(int red, int green, int blue) { | |
| 601 SendMsg("brush(%d,%d,%d)", red, green, blue); | |
| 602 } | |
| 603 | |
| 604 // Set the brush color to the given RGB values. | |
| 605 void ScrollView::Brush(int red, int green, int blue, int alpha) { | |
| 606 SendMsg("brush(%d,%d,%d,%d)", red, green, blue, alpha); | |
| 607 } | |
| 608 | |
| 609 // Set the attributes for future Text(..) calls. | |
| 610 void ScrollView::TextAttributes(const char *font, int pixel_size, bool bold, bool italic, | |
| 611 bool underlined) { | |
| 612 const char *b; | |
| 613 const char *i; | |
| 614 const char *u; | |
| 615 | |
| 616 if (bold) { | |
| 617 b = "true"; | |
| 618 } else { | |
| 619 b = "false"; | |
| 620 } | |
| 621 if (italic) { | |
| 622 i = "true"; | |
| 623 } else { | |
| 624 i = "false"; | |
| 625 } | |
| 626 if (underlined) { | |
| 627 u = "true"; | |
| 628 } else { | |
| 629 u = "false"; | |
| 630 } | |
| 631 SendMsg("textAttributes('%s',%u,%s,%s,%s)", font, pixel_size, b, i, u); | |
| 632 } | |
| 633 | |
| 634 // Draw text at the given coordinates. | |
| 635 void ScrollView::Text(int x, int y, const char *mystring) { | |
| 636 SendMsg("drawText(%d,%d,'%s')", x, TranslateYCoordinate(y), mystring); | |
| 637 } | |
| 638 | |
| 639 // Open and draw an image given a name at (x,y). | |
| 640 void ScrollView::Draw(const char *image, int x_pos, int y_pos) { | |
| 641 SendMsg("openImage('%s')", image); | |
| 642 SendMsg("drawImage('%s',%d,%d)", image, x_pos, TranslateYCoordinate(y_pos)); | |
| 643 } | |
| 644 | |
| 645 // Add new checkboxmenuentry to menubar. | |
| 646 void ScrollView::MenuItem(const char *parent, const char *name, int cmdEvent, bool flag) { | |
| 647 if (parent == nullptr) { | |
| 648 parent = ""; | |
| 649 } | |
| 650 if (flag) { | |
| 651 SendMsg("addMenuBarItem('%s','%s',%d,true)", parent, name, cmdEvent); | |
| 652 } else { | |
| 653 SendMsg("addMenuBarItem('%s','%s',%d,false)", parent, name, cmdEvent); | |
| 654 } | |
| 655 } | |
| 656 | |
| 657 // Add new menuentry to menubar. | |
| 658 void ScrollView::MenuItem(const char *parent, const char *name, int cmdEvent) { | |
| 659 if (parent == nullptr) { | |
| 660 parent = ""; | |
| 661 } | |
| 662 SendMsg("addMenuBarItem('%s','%s',%d)", parent, name, cmdEvent); | |
| 663 } | |
| 664 | |
| 665 // Add new submenu to menubar. | |
| 666 void ScrollView::MenuItem(const char *parent, const char *name) { | |
| 667 if (parent == nullptr) { | |
| 668 parent = ""; | |
| 669 } | |
| 670 SendMsg("addMenuBarItem('%s','%s')", parent, name); | |
| 671 } | |
| 672 | |
| 673 // Add new submenu to popupmenu. | |
| 674 void ScrollView::PopupItem(const char *parent, const char *name) { | |
| 675 if (parent == nullptr) { | |
| 676 parent = ""; | |
| 677 } | |
| 678 SendMsg("addPopupMenuItem('%s','%s')", parent, name); | |
| 679 } | |
| 680 | |
| 681 // Add new submenuentry to popupmenu. | |
| 682 void ScrollView::PopupItem(const char *parent, const char *name, int cmdEvent, const char *value, | |
| 683 const char *desc) { | |
| 684 if (parent == nullptr) { | |
| 685 parent = ""; | |
| 686 } | |
| 687 char *esc = AddEscapeChars(value); | |
| 688 char *esc2 = AddEscapeChars(desc); | |
| 689 SendMsg("addPopupMenuItem('%s','%s',%d,'%s','%s')", parent, name, cmdEvent, esc, esc2); | |
| 690 delete[] esc; | |
| 691 delete[] esc2; | |
| 692 } | |
| 693 | |
| 694 // Send an update message for a single window. | |
| 695 void ScrollView::UpdateWindow() { | |
| 696 SendMsg("update()"); | |
| 697 } | |
| 698 | |
| 699 // Note: this is an update to all windows | |
| 700 void ScrollView::Update() { | |
| 701 std::lock_guard<std::mutex> guard(*svmap_mu); | |
| 702 for (auto &iter : svmap) { | |
| 703 if (iter.second != nullptr) { | |
| 704 iter.second->UpdateWindow(); | |
| 705 } | |
| 706 } | |
| 707 } | |
| 708 | |
| 709 // Set the pen color, using an enum value (e.g. ScrollView::ORANGE) | |
| 710 void ScrollView::Pen(Color color) { | |
| 711 Pen(table_colors[color][0], table_colors[color][1], table_colors[color][2], | |
| 712 table_colors[color][3]); | |
| 713 } | |
| 714 | |
| 715 // Set the brush color, using an enum value (e.g. ScrollView::ORANGE) | |
| 716 void ScrollView::Brush(Color color) { | |
| 717 Brush(table_colors[color][0], table_colors[color][1], table_colors[color][2], | |
| 718 table_colors[color][3]); | |
| 719 } | |
| 720 | |
| 721 // Shows a modal Input Dialog which can return any kind of String | |
| 722 char *ScrollView::ShowInputDialog(const char *msg) { | |
| 723 SendMsg("showInputDialog(\"%s\")", msg); | |
| 724 // wait till an input event (all others are thrown away) | |
| 725 auto ev = AwaitEvent(SVET_INPUT); | |
| 726 char *p = new char[strlen(ev->parameter) + 1]; | |
| 727 strcpy(p, ev->parameter); | |
| 728 return p; | |
| 729 } | |
| 730 | |
| 731 // Shows a modal Yes/No Dialog which will return 'y' or 'n' | |
| 732 int ScrollView::ShowYesNoDialog(const char *msg) { | |
| 733 SendMsg("showYesNoDialog(\"%s\")", msg); | |
| 734 // Wait till an input event (all others are thrown away) | |
| 735 auto ev = AwaitEvent(SVET_INPUT); | |
| 736 int a = ev->parameter[0]; | |
| 737 return a; | |
| 738 } | |
| 739 | |
| 740 // Zoom the window to the rectangle given upper left corner and | |
| 741 // lower right corner. | |
| 742 void ScrollView::ZoomToRectangle(int x1, int y1, int x2, int y2) { | |
| 743 y1 = TranslateYCoordinate(y1); | |
| 744 y2 = TranslateYCoordinate(y2); | |
| 745 SendMsg("zoomRectangle(%d,%d,%d,%d)", std::min(x1, x2), std::min(y1, y2), std::max(x1, x2), | |
| 746 std::max(y1, y2)); | |
| 747 } | |
| 748 | |
| 749 // Send an image of type Pix. | |
| 750 void ScrollView::Draw(Image image, int x_pos, int y_pos) { | |
| 751 l_uint8 *data; | |
| 752 size_t size; | |
| 753 pixWriteMem(&data, &size, image, IFF_PNG); | |
| 754 int base64_len = (size + 2) / 3 * 4; | |
| 755 y_pos = TranslateYCoordinate(y_pos); | |
| 756 SendMsg("readImage(%d,%d,%d)", x_pos, y_pos, base64_len); | |
| 757 // Base64 encode the data. | |
| 758 const char kBase64Table[64] = { | |
| 759 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', | |
| 760 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', | |
| 761 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', | |
| 762 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', | |
| 763 }; | |
| 764 char *base64 = new char[base64_len + 1]; | |
| 765 memset(base64, '=', base64_len); | |
| 766 base64[base64_len] = '\0'; | |
| 767 int remainder = 0; | |
| 768 int bits_left = 0; | |
| 769 int code_len = 0; | |
| 770 for (size_t i = 0; i < size; ++i) { | |
| 771 int code = (data[i] >> (bits_left + 2)) | remainder; | |
| 772 base64[code_len++] = kBase64Table[code & 63]; | |
| 773 bits_left += 2; | |
| 774 remainder = data[i] << (6 - bits_left); | |
| 775 if (bits_left == 6) { | |
| 776 base64[code_len++] = kBase64Table[remainder & 63]; | |
| 777 bits_left = 0; | |
| 778 remainder = 0; | |
| 779 } | |
| 780 } | |
| 781 if (bits_left > 0) { | |
| 782 base64[code_len++] = kBase64Table[remainder & 63]; | |
| 783 } | |
| 784 SendRawMessage(base64); | |
| 785 delete[] base64; | |
| 786 lept_free(data); | |
| 787 } | |
| 788 | |
| 789 // Escapes the ' character with a \, so it can be processed by LUA. | |
| 790 // Note: The caller will have to make sure it deletes the newly allocated item. | |
| 791 char *ScrollView::AddEscapeChars(const char *input) { | |
| 792 const char *nextptr = strchr(input, '\''); | |
| 793 const char *lastptr = input; | |
| 794 char *message = new char[kMaxMsgSize]; | |
| 795 int pos = 0; | |
| 796 while (nextptr != nullptr) { | |
| 797 strncpy(message + pos, lastptr, nextptr - lastptr); | |
| 798 pos += nextptr - lastptr; | |
| 799 message[pos] = '\\'; | |
| 800 pos += 1; | |
| 801 lastptr = nextptr; | |
| 802 nextptr = strchr(nextptr + 1, '\''); | |
| 803 } | |
| 804 strcpy(message + pos, lastptr); | |
| 805 return message; | |
| 806 } | |
| 807 | |
| 808 // Inverse the Y axis if the coordinates are actually inversed. | |
| 809 int ScrollView::TranslateYCoordinate(int y) { | |
| 810 if (!y_axis_is_reversed_) { | |
| 811 return y; | |
| 812 } else { | |
| 813 return y_size_ - y; | |
| 814 } | |
| 815 } | |
| 816 | |
| 817 char ScrollView::Wait() { | |
| 818 // Wait till an input or click event (all others are thrown away) | |
| 819 char ret = '\0'; | |
| 820 SVEventType ev_type = SVET_ANY; | |
| 821 do { | |
| 822 std::unique_ptr<SVEvent> ev(AwaitEvent(SVET_ANY)); | |
| 823 ev_type = ev->type; | |
| 824 if (ev_type == SVET_INPUT) { | |
| 825 ret = ev->parameter[0]; | |
| 826 } | |
| 827 } while (ev_type != SVET_INPUT && ev_type != SVET_CLICK); | |
| 828 return ret; | |
| 829 } | |
| 830 | |
| 831 #endif // !GRAPHICS_DISABLED | |
| 832 | |
| 833 } // namespace tesseract |
