comparison mupdf-source/thirdparty/tesseract/src/ccmain/pgedit.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: pgedit.cpp (Formerly pgeditor.c)
3 * Description: Page structure file editor
4 * Author: Phil Cheatle
5 *
6 *(C) Copyright 1991, Hewlett-Packard Ltd.
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 // Include automatically generated configuration file if running autoconf.
20 #ifdef HAVE_CONFIG_H
21 # include "config_auto.h"
22 #endif
23
24 #include "pgedit.h"
25
26 #include "blread.h"
27 #include "control.h"
28 #include "pageres.h"
29 #include "paramsd.h"
30 #include "scrollview.h"
31 #include "statistc.h"
32 #include "svmnode.h"
33 #include "tesseractclass.h"
34 #include "tordmain.h"
35 #include "werdit.h"
36
37 #include <cctype>
38 #include <cmath>
39 #include <iomanip> // for std::setprecision
40 #include <locale> // for std::locale::classic
41 #include <sstream> // for std::stringstream
42
43 #ifndef GRAPHICS_DISABLED
44 namespace tesseract {
45 # define ASC_HEIGHT (2 * kBlnBaselineOffset + kBlnXHeight)
46 # define X_HEIGHT (kBlnBaselineOffset + kBlnXHeight)
47 # define BL_HEIGHT kBlnBaselineOffset
48 # define DESC_HEIGHT 0
49
50 enum CMD_EVENTS {
51 NULL_CMD_EVENT,
52 CHANGE_DISP_CMD_EVENT,
53 DUMP_WERD_CMD_EVENT,
54 SHOW_POINT_CMD_EVENT,
55 SHOW_BLN_WERD_CMD_EVENT,
56 DEBUG_WERD_CMD_EVENT,
57 BLAMER_CMD_EVENT,
58 BOUNDING_BOX_CMD_EVENT,
59 CORRECT_TEXT_CMD_EVENT,
60 POLYGONAL_CMD_EVENT,
61 BL_NORM_CMD_EVENT,
62 BITMAP_CMD_EVENT,
63 IMAGE_CMD_EVENT,
64 BLOCKS_CMD_EVENT,
65 BASELINES_CMD_EVENT,
66 UNIFORM_DISP_CMD_EVENT,
67 REFRESH_CMD_EVENT,
68 QUIT_CMD_EVENT,
69 RECOG_WERDS,
70 RECOG_PSEUDO,
71 SHOW_BLOB_FEATURES,
72 SHOW_SUBSCRIPT_CMD_EVENT,
73 SHOW_SUPERSCRIPT_CMD_EVENT,
74 SHOW_ITALIC_CMD_EVENT,
75 SHOW_BOLD_CMD_EVENT,
76 SHOW_UNDERLINE_CMD_EVENT,
77 SHOW_FIXEDPITCH_CMD_EVENT,
78 SHOW_SERIF_CMD_EVENT,
79 SHOW_SMALLCAPS_CMD_EVENT,
80 SHOW_DROPCAPS_CMD_EVENT,
81 };
82
83 enum ColorationMode {
84 CM_RAINBOW,
85 CM_SUBSCRIPT,
86 CM_SUPERSCRIPT,
87 CM_ITALIC,
88 CM_BOLD,
89 CM_UNDERLINE,
90 CM_FIXEDPITCH,
91 CM_SERIF,
92 CM_SMALLCAPS,
93 CM_DROPCAPS
94 };
95
96 /*
97 *
98 * Some global data
99 *
100 */
101
102 static ScrollView *image_win;
103 static ParamsEditor *pe;
104 static bool stillRunning = false;
105
106 static ScrollView *bln_word_window = nullptr; // baseline norm words
107
108 static CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; // selected words op
109
110 static bool recog_done = false; // recog_all_words was called
111
112 // These variables should remain global, since they are only used for the
113 // debug mode (in which only a single Tesseract thread/instance will exist).
114 static std::bitset<16> word_display_mode;
115 static ColorationMode color_mode = CM_RAINBOW;
116 static bool display_image = false;
117 static bool display_blocks = false;
118 static bool display_baselines = false;
119
120 static PAGE_RES *current_page_res = nullptr;
121
122 STRING_VAR(editor_image_win_name, "EditorImage", "Editor image window name");
123 INT_VAR(editor_image_xpos, 590, "Editor image X Pos");
124 INT_VAR(editor_image_ypos, 10, "Editor image Y Pos");
125 static INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar");
126 INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, "Word bounding box colour");
127 INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, "Blob bounding box colour");
128
129 STRING_VAR(editor_word_name, "BlnWords", "BL normalized word window");
130 INT_VAR(editor_word_xpos, 60, "Word window X Pos");
131 INT_VAR(editor_word_ypos, 510, "Word window Y Pos");
132 INT_VAR(editor_word_height, 240, "Word window height");
133 INT_VAR(editor_word_width, 655, "Word window width");
134
135 /**
136 * show_point()
137 *
138 * Show coords of point, blob bounding box, word bounding box and offset from
139 * row baseline
140 */
141
142 static void show_point(PAGE_RES *page_res, float x, float y) {
143 FCOORD pt(x, y);
144 PAGE_RES_IT pr_it(page_res);
145
146 std::stringstream msg;
147 msg.imbue(std::locale::classic());
148 msg << std::fixed << std::setprecision(3) << "Pt:(" << x << ", " << y << ") ";
149
150 for (WERD_RES *word = pr_it.word(); word != nullptr; word = pr_it.forward()) {
151 if (pr_it.row() != pr_it.prev_row() && pr_it.row()->row->bounding_box().contains(pt)) {
152 msg << "BL(x)=" << pr_it.row()->row->base_line(x) << ' ';
153 }
154 if (word->word->bounding_box().contains(pt)) {
155 TBOX box = word->word->bounding_box();
156 msg << "Wd(" << box.left() << ", " << box.bottom() << ")/("
157 << box.right() << ", " << box.top() << ") ";
158 C_BLOB_IT cblob_it(word->word->cblob_list());
159 for (cblob_it.mark_cycle_pt(); !cblob_it.cycled_list(); cblob_it.forward()) {
160 C_BLOB *cblob = cblob_it.data();
161 box = cblob->bounding_box();
162 if (box.contains(pt)) {
163 msg << "CBlb(" << box.left() << ", " << box.bottom() << ")/("
164 << box.right() << ", " << box.top() << ") ";
165 }
166 }
167 }
168 }
169 image_win->AddMessage(msg.str().c_str());
170 }
171
172 /**
173 * pgeditor_msg()
174 *
175 * Display a message - in the command window if there is one, or to stdout
176 */
177
178 static void pgeditor_msg( // message display
179 const char *msg) {
180 image_win->AddMessage(msg);
181 }
182
183 class BlnEventHandler : public SVEventHandler {
184 public:
185 void Notify(const SVEvent *sv_event) override {
186 if (sv_event->type == SVET_DESTROY) {
187 bln_word_window = nullptr;
188 } else if (sv_event->type == SVET_CLICK) {
189 show_point(current_page_res, sv_event->x, sv_event->y);
190 }
191 }
192 };
193
194 /**
195 * bln_word_window_handle()
196 *
197 * @return a WINDOW for the word window, creating it if necessary
198 */
199 static ScrollView *bln_word_window_handle() { // return handle
200 // not opened yet
201 if (bln_word_window == nullptr) {
202 pgeditor_msg("Creating BLN word window...");
203 bln_word_window = new ScrollView(editor_word_name.c_str(), editor_word_xpos, editor_word_ypos,
204 editor_word_width, editor_word_height, 4000, 4000, true);
205 auto *a = new BlnEventHandler();
206 bln_word_window->AddEventHandler(a);
207 pgeditor_msg("Creating BLN word window...Done");
208 }
209 return bln_word_window;
210 }
211
212 /**
213 * build_image_window()
214 *
215 * Destroy the existing image window if there is one. Work out how big the
216 * new window needs to be. Create it and re-display.
217 */
218
219 static void build_image_window(int width, int height) {
220 delete image_win;
221 image_win = new ScrollView(editor_image_win_name.c_str(), editor_image_xpos, editor_image_ypos,
222 width + 1, height + editor_image_menuheight + 1, width, height, true);
223 }
224
225 /**
226 * display_bln_lines()
227 *
228 * Display normalized baseline, x-height, ascender limit and descender limit
229 */
230
231 static void display_bln_lines(ScrollView *window, ScrollView::Color colour, float scale_factor,
232 float y_offset, float minx, float maxx) {
233 window->Pen(colour);
234 window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, maxx,
235 y_offset + scale_factor * DESC_HEIGHT);
236 window->Line(minx, y_offset + scale_factor * BL_HEIGHT, maxx,
237 y_offset + scale_factor * BL_HEIGHT);
238 window->Line(minx, y_offset + scale_factor * X_HEIGHT, maxx, y_offset + scale_factor * X_HEIGHT);
239 window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, maxx,
240 y_offset + scale_factor * ASC_HEIGHT);
241 }
242
243 /**
244 * notify()
245 *
246 * Event handler that processes incoming events, either forwarding
247 * them to process_cmd_win_event or process_image_event.
248 *
249 */
250
251 void PGEventHandler::Notify(const SVEvent *event) {
252 char myval = '0';
253 if (event->type == SVET_POPUP) {
254 pe->Notify(event);
255 } // These are handled by ParamsEditor
256 else if (event->type == SVET_EXIT) {
257 stillRunning = false;
258 } else if (event->type == SVET_MENU) {
259 if (strcmp(event->parameter, "true") == 0) {
260 myval = 'T';
261 } else if (strcmp(event->parameter, "false") == 0) {
262 myval = 'F';
263 }
264 tess_->process_cmd_win_event(event->command_id, &myval);
265 } else {
266 tess_->process_image_event(*event);
267 }
268 }
269
270 /**
271 * build_menu()
272 *
273 * Construct the menu tree used by the command window
274 */
275 SVMenuNode *Tesseract::build_menu_new() {
276 SVMenuNode *parent_menu;
277 auto *root_menu_item = new SVMenuNode();
278
279 SVMenuNode *modes_menu_item = root_menu_item->AddChild("MODES");
280
281 modes_menu_item->AddChild("Change Display", CHANGE_DISP_CMD_EVENT);
282 modes_menu_item->AddChild("Dump Word", DUMP_WERD_CMD_EVENT);
283 modes_menu_item->AddChild("Show Point", SHOW_POINT_CMD_EVENT);
284 modes_menu_item->AddChild("Show BL Norm Word", SHOW_BLN_WERD_CMD_EVENT);
285 modes_menu_item->AddChild("Config Words", DEBUG_WERD_CMD_EVENT);
286 modes_menu_item->AddChild("Recog Words", RECOG_WERDS);
287 modes_menu_item->AddChild("Recog Blobs", RECOG_PSEUDO);
288 modes_menu_item->AddChild("Show Blob Features", SHOW_BLOB_FEATURES);
289
290 parent_menu = root_menu_item->AddChild("DISPLAY");
291
292 parent_menu->AddChild("Blamer", BLAMER_CMD_EVENT, false);
293 parent_menu->AddChild("Bounding Boxes", BOUNDING_BOX_CMD_EVENT, false);
294 parent_menu->AddChild("Correct Text", CORRECT_TEXT_CMD_EVENT, false);
295 parent_menu->AddChild("Polygonal Approx", POLYGONAL_CMD_EVENT, false);
296 parent_menu->AddChild("Baseline Normalized", BL_NORM_CMD_EVENT, false);
297 parent_menu->AddChild("Edge Steps", BITMAP_CMD_EVENT, true);
298 parent_menu->AddChild("Subscripts", SHOW_SUBSCRIPT_CMD_EVENT);
299 parent_menu->AddChild("Superscripts", SHOW_SUPERSCRIPT_CMD_EVENT);
300 parent_menu->AddChild("Italics", SHOW_ITALIC_CMD_EVENT);
301 parent_menu->AddChild("Bold", SHOW_BOLD_CMD_EVENT);
302 parent_menu->AddChild("Underline", SHOW_UNDERLINE_CMD_EVENT);
303 parent_menu->AddChild("FixedPitch", SHOW_FIXEDPITCH_CMD_EVENT);
304 parent_menu->AddChild("Serifs", SHOW_SERIF_CMD_EVENT);
305 parent_menu->AddChild("SmallCaps", SHOW_SMALLCAPS_CMD_EVENT);
306 parent_menu->AddChild("DropCaps", SHOW_DROPCAPS_CMD_EVENT);
307
308 parent_menu = root_menu_item->AddChild("OTHER");
309
310 parent_menu->AddChild("Quit", QUIT_CMD_EVENT);
311 parent_menu->AddChild("Show Image", IMAGE_CMD_EVENT, false);
312 parent_menu->AddChild("ShowBlock Outlines", BLOCKS_CMD_EVENT, false);
313 parent_menu->AddChild("Show Baselines", BASELINES_CMD_EVENT, false);
314 parent_menu->AddChild("Uniform Display", UNIFORM_DISP_CMD_EVENT);
315 parent_menu->AddChild("Refresh Display", REFRESH_CMD_EVENT);
316
317 return root_menu_item;
318 }
319
320 /**
321 * do_re_display()
322 *
323 * Redisplay page
324 */
325 void Tesseract::do_re_display(bool (tesseract::Tesseract::*word_painter)(PAGE_RES_IT *pr_it)) {
326 int block_count = 1;
327
328 image_win->Clear();
329 if (display_image) {
330 image_win->Draw(pix_binary_, 0, 0);
331 }
332
333 image_win->Brush(ScrollView::NONE);
334 PAGE_RES_IT pr_it(current_page_res);
335 for (WERD_RES *word = pr_it.word(); word != nullptr; word = pr_it.forward()) {
336 (this->*word_painter)(&pr_it);
337 if (display_baselines && pr_it.row() != pr_it.prev_row()) {
338 pr_it.row()->row->plot_baseline(image_win, ScrollView::GREEN);
339 }
340 if (display_blocks && pr_it.block() != pr_it.prev_block()) {
341 pr_it.block()->block->pdblk.plot(image_win, block_count++, ScrollView::RED);
342 }
343 }
344 image_win->Update();
345 }
346
347 /**
348 * pgeditor_main()
349 *
350 * Top level editor operation:
351 * Setup a new window and an according event handler
352 *
353 */
354
355 void Tesseract::pgeditor_main(int width, int height, PAGE_RES *page_res) {
356 current_page_res = page_res;
357 if (current_page_res->block_res_list.empty()) {
358 return;
359 }
360
361 recog_done = false;
362 stillRunning = true;
363
364 build_image_window(width, height);
365 word_display_mode.set(DF_EDGE_STEP);
366 do_re_display(&tesseract::Tesseract::word_set_display);
367 # ifndef GRAPHICS_DISABLED
368 pe = new ParamsEditor(this, image_win);
369 # endif
370 PGEventHandler pgEventHandler(this);
371
372 image_win->AddEventHandler(&pgEventHandler);
373 image_win->AddMessageBox();
374
375 SVMenuNode *svMenuRoot = build_menu_new();
376
377 svMenuRoot->BuildMenu(image_win);
378 image_win->SetVisible(true);
379
380 image_win->AwaitEvent(SVET_DESTROY);
381 image_win->AddEventHandler(nullptr);
382 }
383
384 /**
385 * process_cmd_win_event()
386 *
387 * Process a command returned from the command window
388 * (Just call the appropriate command handler)
389 */
390
391 bool Tesseract::process_cmd_win_event( // UI command semantics
392 int32_t cmd_event, // which menu item?
393 char *new_value // any prompt data
394 ) {
395 char msg[160];
396 bool exit = false;
397
398 color_mode = CM_RAINBOW;
399
400 // Run recognition on the full page if needed.
401 switch (cmd_event) {
402 case BLAMER_CMD_EVENT:
403 case SHOW_SUBSCRIPT_CMD_EVENT:
404 case SHOW_SUPERSCRIPT_CMD_EVENT:
405 case SHOW_ITALIC_CMD_EVENT:
406 case SHOW_BOLD_CMD_EVENT:
407 case SHOW_UNDERLINE_CMD_EVENT:
408 case SHOW_FIXEDPITCH_CMD_EVENT:
409 case SHOW_SERIF_CMD_EVENT:
410 case SHOW_SMALLCAPS_CMD_EVENT:
411 case SHOW_DROPCAPS_CMD_EVENT:
412 if (!recog_done) {
413 recog_all_words(current_page_res, nullptr, nullptr, nullptr, 0);
414 recog_done = true;
415 }
416 break;
417 default:
418 break;
419 }
420
421 char *parameter;
422
423 switch (cmd_event) {
424 case NULL_CMD_EVENT:
425 break;
426
427 case CHANGE_DISP_CMD_EVENT:
428 case DUMP_WERD_CMD_EVENT:
429 case SHOW_POINT_CMD_EVENT:
430 case SHOW_BLN_WERD_CMD_EVENT:
431 case RECOG_WERDS:
432 case RECOG_PSEUDO:
433 case SHOW_BLOB_FEATURES:
434 mode = static_cast<CMD_EVENTS>(cmd_event);
435 break;
436 case DEBUG_WERD_CMD_EVENT:
437 mode = DEBUG_WERD_CMD_EVENT;
438 parameter = image_win->ShowInputDialog("Config File Name");
439 word_config_ = parameter;
440 delete[] parameter;
441 break;
442 case BOUNDING_BOX_CMD_EVENT:
443 if (new_value[0] == 'T') {
444 word_display_mode.set(DF_BOX);
445 } else {
446 word_display_mode.reset(DF_BOX);
447 }
448 mode = CHANGE_DISP_CMD_EVENT;
449 break;
450 case BLAMER_CMD_EVENT:
451 if (new_value[0] == 'T') {
452 word_display_mode.set(DF_BLAMER);
453 } else {
454 word_display_mode.reset(DF_BLAMER);
455 }
456 do_re_display(&tesseract::Tesseract::word_display);
457 mode = CHANGE_DISP_CMD_EVENT;
458 break;
459 case CORRECT_TEXT_CMD_EVENT:
460 if (new_value[0] == 'T') {
461 word_display_mode.set(DF_TEXT);
462 } else {
463 word_display_mode.reset(DF_TEXT);
464 }
465 mode = CHANGE_DISP_CMD_EVENT;
466 break;
467 case POLYGONAL_CMD_EVENT:
468 if (new_value[0] == 'T') {
469 word_display_mode.set(DF_POLYGONAL);
470 } else {
471 word_display_mode.reset(DF_POLYGONAL);
472 }
473 mode = CHANGE_DISP_CMD_EVENT;
474 break;
475 case BL_NORM_CMD_EVENT:
476 if (new_value[0] == 'T') {
477 word_display_mode.set(DF_BN_POLYGONAL);
478 } else {
479 word_display_mode.reset(DF_BN_POLYGONAL);
480 }
481 mode = CHANGE_DISP_CMD_EVENT;
482 break;
483 case BITMAP_CMD_EVENT:
484 if (new_value[0] == 'T') {
485 word_display_mode.set(DF_EDGE_STEP);
486 } else {
487 word_display_mode.reset(DF_EDGE_STEP);
488 }
489 mode = CHANGE_DISP_CMD_EVENT;
490 break;
491 case UNIFORM_DISP_CMD_EVENT:
492 do_re_display(&tesseract::Tesseract::word_set_display);
493 break;
494 case IMAGE_CMD_EVENT:
495 display_image = (new_value[0] == 'T');
496 do_re_display(&tesseract::Tesseract::word_display);
497 break;
498 case BLOCKS_CMD_EVENT:
499 display_blocks = (new_value[0] == 'T');
500 do_re_display(&tesseract::Tesseract::word_display);
501 break;
502 case BASELINES_CMD_EVENT:
503 display_baselines = (new_value[0] == 'T');
504 do_re_display(&tesseract::Tesseract::word_display);
505 break;
506 case SHOW_SUBSCRIPT_CMD_EVENT:
507 color_mode = CM_SUBSCRIPT;
508 do_re_display(&tesseract::Tesseract::word_display);
509 break;
510 case SHOW_SUPERSCRIPT_CMD_EVENT:
511 color_mode = CM_SUPERSCRIPT;
512 do_re_display(&tesseract::Tesseract::word_display);
513 break;
514 case SHOW_ITALIC_CMD_EVENT:
515 color_mode = CM_ITALIC;
516 do_re_display(&tesseract::Tesseract::word_display);
517 break;
518 case SHOW_BOLD_CMD_EVENT:
519 color_mode = CM_BOLD;
520 do_re_display(&tesseract::Tesseract::word_display);
521 break;
522 case SHOW_UNDERLINE_CMD_EVENT:
523 color_mode = CM_UNDERLINE;
524 do_re_display(&tesseract::Tesseract::word_display);
525 break;
526 case SHOW_FIXEDPITCH_CMD_EVENT:
527 color_mode = CM_FIXEDPITCH;
528 do_re_display(&tesseract::Tesseract::word_display);
529 break;
530 case SHOW_SERIF_CMD_EVENT:
531 color_mode = CM_SERIF;
532 do_re_display(&tesseract::Tesseract::word_display);
533 break;
534 case SHOW_SMALLCAPS_CMD_EVENT:
535 color_mode = CM_SMALLCAPS;
536 do_re_display(&tesseract::Tesseract::word_display);
537 break;
538 case SHOW_DROPCAPS_CMD_EVENT:
539 color_mode = CM_DROPCAPS;
540 do_re_display(&tesseract::Tesseract::word_display);
541 break;
542 case REFRESH_CMD_EVENT:
543 do_re_display(&tesseract::Tesseract::word_display);
544 break;
545 case QUIT_CMD_EVENT:
546 exit = true;
547 ScrollView::Exit();
548 break;
549
550 default:
551 snprintf(msg, sizeof(msg), "Unrecognised event %" PRId32 "(%s)", cmd_event, new_value);
552 image_win->AddMessage(msg);
553 break;
554 }
555 return exit;
556 }
557
558 /**
559 * process_image_event()
560 *
561 * User has done something in the image window - mouse down or up. Work out
562 * what it is and do something with it.
563 * If DOWN - just remember where it was.
564 * If UP - for each word in the selected area do the operation defined by
565 * the current mode.
566 */
567 void Tesseract::process_image_event( // action in image win
568 const SVEvent &event) {
569 // The following variable should remain static, since it is used by
570 // debug editor, which uses a single Tesseract instance.
571 static ICOORD down;
572 ICOORD up;
573 TBOX selection_box;
574 char msg[80];
575
576 switch (event.type) {
577 case SVET_SELECTION:
578 if (event.type == SVET_SELECTION) {
579 down.set_x(event.x + event.x_size);
580 down.set_y(event.y + event.y_size);
581 if (mode == SHOW_POINT_CMD_EVENT) {
582 show_point(current_page_res, event.x, event.y);
583 }
584 }
585
586 up.set_x(event.x);
587 up.set_y(event.y);
588
589 selection_box = TBOX(down, up);
590
591 switch (mode) {
592 case CHANGE_DISP_CMD_EVENT:
593 process_selected_words(current_page_res, selection_box,
594 &tesseract::Tesseract::word_blank_and_set_display);
595 break;
596 case DUMP_WERD_CMD_EVENT:
597 process_selected_words(current_page_res, selection_box,
598 &tesseract::Tesseract::word_dumper);
599 break;
600 case SHOW_BLN_WERD_CMD_EVENT:
601 process_selected_words(current_page_res, selection_box,
602 &tesseract::Tesseract::word_bln_display);
603 break;
604 case DEBUG_WERD_CMD_EVENT:
605 debug_word(current_page_res, selection_box);
606 break;
607 case SHOW_POINT_CMD_EVENT:
608 break; // ignore up event
609
610 case RECOG_WERDS:
611 # ifndef DISABLED_LEGACY_ENGINE
612 image_win->AddMessage("Recogging selected words");
613 this->process_selected_words(current_page_res, selection_box,
614 &Tesseract::recog_interactive);
615 # endif // ndef DISABLED_LEGACY_ENGINE
616 break;
617 case RECOG_PSEUDO:
618 image_win->AddMessage("Recogging selected blobs");
619 recog_pseudo_word(current_page_res, selection_box);
620 break;
621 case SHOW_BLOB_FEATURES:
622 blob_feature_display(current_page_res, selection_box);
623 break;
624
625 default:
626 snprintf(msg, sizeof(msg), "Mode %d not yet implemented", mode);
627 image_win->AddMessage(msg);
628 break;
629 }
630 default:
631 break;
632 }
633 }
634
635 /**
636 * debug_word
637 *
638 * Process the whole image, but load word_config_ for the selected word(s).
639 */
640 void Tesseract::debug_word(PAGE_RES *page_res, const TBOX &selection_box) {
641 # ifndef DISABLED_LEGACY_ENGINE
642 ResetAdaptiveClassifier();
643 # endif
644 recog_all_words(page_res, nullptr, &selection_box, word_config_.c_str(), 0);
645 }
646
647 /**********************************************************************
648 * WERD PROCESSOR FUNCTIONS
649 * ========================
650 *
651 * These routines are invoked by one or more of:
652 * process_all_words()
653 * process_selected_words()
654 * or
655 * process_all_words_it()
656 * process_selected_words_it()
657 * for each word to be processed
658 **********************************************************************/
659
660 /**
661 * word_blank_and_set_display() Word processor
662 *
663 * Blank display of word then redisplay word according to current display mode
664 * settings
665 */
666
667 bool Tesseract::word_blank_and_set_display(PAGE_RES_IT *pr_it) {
668 pr_it->word()->word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
669 return word_set_display(pr_it);
670 }
671
672 /**
673 * word_bln_display()
674 *
675 * Normalize word and display in word window
676 */
677 bool Tesseract::word_bln_display(PAGE_RES_IT *pr_it) {
678 WERD_RES *word_res = pr_it->word();
679 if (word_res->chopped_word == nullptr) {
680 // Setup word normalization parameters.
681 word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, nullptr,
682 classify_bln_numeric_mode, textord_use_cjk_fp_model,
683 poly_allow_detailed_fx, pr_it->row()->row, pr_it->block()->block);
684 }
685 bln_word_window_handle()->Clear();
686 display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, 1.0, 0.0f, -1000.0f, 1000.0f);
687 C_BLOB_IT it(word_res->word->cblob_list());
688 ScrollView::Color color = WERD::NextColor(ScrollView::BLACK);
689 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
690 it.data()->plot_normed(word_res->denorm, color, ScrollView::BROWN, bln_word_window_handle());
691 color = WERD::NextColor(color);
692 }
693 bln_word_window_handle()->Update();
694 return true;
695 }
696
697 /**
698 * word_display() Word Processor
699 *
700 * Display a word according to its display modes
701 */
702 bool Tesseract::word_display(PAGE_RES_IT *pr_it) {
703 WERD_RES *word_res = pr_it->word();
704 WERD *word = word_res->word;
705 TBOX word_bb; // word bounding box
706 bool displayed_something = false;
707
708 if (color_mode != CM_RAINBOW && word_res->box_word != nullptr) {
709 # ifndef DISABLED_LEGACY_ENGINE
710 BoxWord *box_word = word_res->box_word;
711 WERD_CHOICE *best_choice = word_res->best_choice;
712 int length = box_word->length();
713 if (word_res->fontinfo == nullptr) {
714 return false;
715 }
716 const FontInfo &font_info = *word_res->fontinfo;
717 for (int i = 0; i < length; ++i) {
718 ScrollView::Color color = ScrollView::GREEN;
719 switch (color_mode) {
720 case CM_SUBSCRIPT:
721 if (best_choice->BlobPosition(i) == SP_SUBSCRIPT) {
722 color = ScrollView::RED;
723 }
724 break;
725 case CM_SUPERSCRIPT:
726 if (best_choice->BlobPosition(i) == SP_SUPERSCRIPT) {
727 color = ScrollView::RED;
728 }
729 break;
730 case CM_ITALIC:
731 if (font_info.is_italic()) {
732 color = ScrollView::RED;
733 }
734 break;
735 case CM_BOLD:
736 if (font_info.is_bold()) {
737 color = ScrollView::RED;
738 }
739 break;
740 case CM_FIXEDPITCH:
741 if (font_info.is_fixed_pitch()) {
742 color = ScrollView::RED;
743 }
744 break;
745 case CM_SERIF:
746 if (font_info.is_serif()) {
747 color = ScrollView::RED;
748 }
749 break;
750 case CM_SMALLCAPS:
751 if (word_res->small_caps) {
752 color = ScrollView::RED;
753 }
754 break;
755 case CM_DROPCAPS:
756 if (best_choice->BlobPosition(i) == SP_DROPCAP) {
757 color = ScrollView::RED;
758 }
759 break;
760 // TODO(rays) underline is currently completely unsupported.
761 case CM_UNDERLINE:
762 default:
763 break;
764 }
765 image_win->Pen(color);
766 TBOX box = box_word->BlobBox(i);
767 image_win->Rectangle(box.left(), box.bottom(), box.right(), box.top());
768 }
769 return true;
770 # else
771 return false;
772 # endif // ndef DISABLED_LEGACY_ENGINE
773 }
774 /*
775 Note the double coercions of(COLOUR)((int32_t)editor_image_word_bb_color)
776 etc. are to keep the compiler happy.
777 */
778 // display bounding box
779 if (word->display_flag(DF_BOX)) {
780 word->bounding_box().plot(image_win,
781 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color),
782 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color));
783
784 auto c = static_cast<ScrollView::Color>((int32_t)editor_image_blob_bb_color);
785 image_win->Pen(c);
786 // cblob iterator
787 C_BLOB_IT c_it(word->cblob_list());
788 for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) {
789 c_it.data()->bounding_box().plot(image_win);
790 }
791 displayed_something = true;
792 }
793
794 // display edge steps
795 if (word->display_flag(DF_EDGE_STEP)) { // edgesteps available
796 word->plot(image_win); // rainbow colors
797 displayed_something = true;
798 }
799
800 // display poly approx
801 if (word->display_flag(DF_POLYGONAL)) {
802 // need to convert
803 TWERD *tword = TWERD::PolygonalCopy(poly_allow_detailed_fx, word);
804 tword->plot(image_win);
805 delete tword;
806 displayed_something = true;
807 }
808
809 // Display correct text and blamer information.
810 std::string text;
811 std::string blame;
812 if (word->display_flag(DF_TEXT) && word->text() != nullptr) {
813 text = word->text();
814 }
815 if (word->display_flag(DF_BLAMER) &&
816 !(word_res->blamer_bundle != nullptr &&
817 word_res->blamer_bundle->incorrect_result_reason() == IRR_CORRECT)) {
818 text = "";
819 const BlamerBundle *blamer_bundle = word_res->blamer_bundle;
820 if (blamer_bundle == nullptr) {
821 text += "NULL";
822 } else {
823 text = blamer_bundle->TruthString();
824 }
825 text += " -> ";
826 std::string best_choice_str;
827 if (word_res->best_choice == nullptr) {
828 best_choice_str = "NULL";
829 } else {
830 word_res->best_choice->string_and_lengths(&best_choice_str, nullptr);
831 }
832 text += best_choice_str;
833 IncorrectResultReason reason =
834 (blamer_bundle == nullptr) ? IRR_PAGE_LAYOUT : blamer_bundle->incorrect_result_reason();
835 ASSERT_HOST(reason < IRR_NUM_REASONS);
836 blame += " [";
837 blame += BlamerBundle::IncorrectReasonName(reason);
838 blame += "]";
839 }
840 if (text.length() > 0) {
841 word_bb = word->bounding_box();
842 image_win->Pen(ScrollView::RED);
843 auto word_height = word_bb.height();
844 int text_height = word_height / 2;
845 if (text_height > 20) {
846 text_height = 20;
847 }
848 image_win->TextAttributes("Arial", text_height, false, false, false);
849 // from bot left
850 float shift = (word_height < word_bb.width()) ? 0.25f * word_height : 0.0f;
851 image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height, text.c_str());
852 if (blame.length() > 0) {
853 image_win->Text(word_bb.left() + shift, word_bb.bottom() + 0.25 * word_height - text_height,
854 blame.c_str());
855 }
856
857 displayed_something = true;
858 }
859
860 if (!displayed_something) { // display BBox anyway
861 word->bounding_box().plot(image_win,
862 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color),
863 static_cast<ScrollView::Color>((int32_t)editor_image_word_bb_color));
864 }
865 return true;
866 }
867 } // namespace tesseract
868 #endif // !GRAPHICS_DISABLED
869
870 namespace tesseract {
871 /**
872 * word_dumper()
873 *
874 * Dump members to the debug window
875 */
876 bool Tesseract::word_dumper(PAGE_RES_IT *pr_it) {
877 if (pr_it->block()->block != nullptr) {
878 tprintf("\nBlock data...\n");
879 pr_it->block()->block->print(nullptr, false);
880 }
881 tprintf("\nRow data...\n");
882 pr_it->row()->row->print(nullptr);
883 tprintf("\nWord data...\n");
884 WERD_RES *word_res = pr_it->word();
885 word_res->word->print();
886 if (word_res->blamer_bundle != nullptr && wordrec_debug_blamer &&
887 word_res->blamer_bundle->incorrect_result_reason() != IRR_CORRECT) {
888 tprintf("Current blamer debug: %s\n", word_res->blamer_bundle->debug().c_str());
889 }
890 return true;
891 }
892
893 #ifndef GRAPHICS_DISABLED
894 /**
895 * word_set_display() Word processor
896 *
897 * Display word according to current display mode settings
898 */
899 bool Tesseract::word_set_display(PAGE_RES_IT *pr_it) {
900 WERD *word = pr_it->word()->word;
901 word->set_display_flag(DF_BOX, word_display_mode[DF_BOX]);
902 word->set_display_flag(DF_TEXT, word_display_mode[DF_TEXT]);
903 word->set_display_flag(DF_POLYGONAL, word_display_mode[DF_POLYGONAL]);
904 word->set_display_flag(DF_EDGE_STEP, word_display_mode[DF_EDGE_STEP]);
905 word->set_display_flag(DF_BN_POLYGONAL, word_display_mode[DF_BN_POLYGONAL]);
906 word->set_display_flag(DF_BLAMER, word_display_mode[DF_BLAMER]);
907 return word_display(pr_it);
908 }
909
910 // page_res is non-const because the iterator doesn't know if you are going
911 // to change the items it points to! Really a const here though.
912 void Tesseract::blob_feature_display(PAGE_RES *page_res, const TBOX &selection_box) {
913 # ifndef DISABLED_LEGACY_ENGINE
914 PAGE_RES_IT *it = make_pseudo_word(page_res, selection_box);
915 if (it != nullptr) {
916 WERD_RES *word_res = it->word();
917 word_res->x_height = it->row()->row->x_height();
918 word_res->SetupForRecognition(unicharset, this, BestPix(), tessedit_ocr_engine_mode, nullptr,
919 classify_bln_numeric_mode, textord_use_cjk_fp_model,
920 poly_allow_detailed_fx, it->row()->row, it->block()->block);
921 TWERD *bln_word = word_res->chopped_word;
922 TBLOB *bln_blob = bln_word->blobs[0];
923 INT_FX_RESULT_STRUCT fx_info;
924 std::vector<INT_FEATURE_STRUCT> bl_features;
925 std::vector<INT_FEATURE_STRUCT> cn_features;
926 Classify::ExtractFeatures(*bln_blob, classify_nonlinear_norm, &bl_features, &cn_features,
927 &fx_info, nullptr);
928 // Display baseline features.
929 ScrollView *bl_win = CreateFeatureSpaceWindow("BL Features", 512, 0);
930 ClearFeatureSpaceWindow(baseline, bl_win);
931 for (auto &bl_feature : bl_features) {
932 RenderIntFeature(bl_win, &bl_feature, ScrollView::GREEN);
933 }
934 bl_win->Update();
935 // Display cn features.
936 ScrollView *cn_win = CreateFeatureSpaceWindow("CN Features", 512, 0);
937 ClearFeatureSpaceWindow(character, cn_win);
938 for (auto &cn_feature : cn_features) {
939 RenderIntFeature(cn_win, &cn_feature, ScrollView::GREEN);
940 }
941 cn_win->Update();
942
943 it->DeleteCurrentWord();
944 delete it;
945 }
946 # endif // ndef DISABLED_LEGACY_ENGINE
947 }
948
949 #endif // !GRAPHICS_DISABLED
950
951 } // namespace tesseract