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