• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**********************************************************************
2  * File:        pgedit.cpp (Formerly pgeditor.c)
3  * Description: Page structure file editor
4  * Author:      Phil Cheatle
5  * Created:     Thu Oct 10 16:25:24 BST 1991
6  *
7  *(C) Copyright 1991, Hewlett-Packard Ltd.
8  ** Licensed under the Apache License, Version 2.0(the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http:// www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include          "pgedit.h"
21 
22 #include          <ctype.h>
23 #include          <math.h>
24 
25 #include          "genblob.h"
26 #include          "tessio.h"
27 #include          "tessout.h"
28 #include          "tordmain.h"
29 #include          "statistc.h"
30 #include          "debugwin.h"
31 #include          "svshowim.h"
32 #include          "mainblk.h"
33 #include          "varabled.h"
34 #include          "string.h"
35 
36 #include          "scrollview.h"
37 #include          "svmnode.h"
38 
39 #include          "control.h"
40 #include "tesseractclass.h"
41 
42 #include          "blread.h"
43 
44 #ifndef GRAPHICS_DISABLED
45 #define ASC_HEIGHT     (2 * bln_baseline_offset + bln_x_height)
46 #define X_HEIGHT       (bln_baseline_offset + bln_x_height)
47 #define BL_HEIGHT     bln_baseline_offset
48 #define DESC_HEIGHT     0
49 #define MAXSPACING      128      /*max expected spacing in pix */
50 
51 const ERRCODE EMPTYBLOCKLIST = "No blocks to edit";
52 extern IMAGE page_image;
53 
54 enum CMD_EVENTS
55 {
56   NULL_CMD_EVENT,
57   DELETE_CMD_EVENT,
58   COPY_CMD_EVENT,
59   CHANGE_DISP_CMD_EVENT,
60   CHANGE_TEXT_CMD_EVENT,
61   TOGGLE_SEG_CMD_EVENT,
62   DUMP_WERD_CMD_EVENT,
63   SHOW_POINT_CMD_EVENT,
64   ROW_SPACE_STAT_CMD_EVENT,
65   BLOCK_SPACE_STAT_CMD_EVENT,
66   SHOW_BLN_WERD_CMD_EVENT,
67   SEGMENT_WERD_CMD_EVENT,
68   BOUNDING_BOX_CMD_EVENT,
69   CORRECT_TEXT_CMD_EVENT,
70   POLYGONAL_CMD_EVENT,
71   BL_NORM_CMD_EVENT,
72   BITMAP_CMD_EVENT,
73   TIDY_CMD_EVENT,
74   VIEW_CMD_EVENT,
75   IMAGE_CMD_EVENT,
76   BLOCKS_CMD_EVENT,
77   BASELINES_CMD_EVENT,
78   WRITE_CMD_EVENT,
79   NEW_SOURCE_CMD_EVENT,
80   UNIFORM_DISP_CMD_EVENT,
81   REFRESH_CMD_EVENT,
82   QUIT_CMD_EVENT,
83   RECOG_WERDS,
84   RECOG_PSEUDO
85 };
86 
87 /**********************************************************************
88  *
89  *  Some global data
90  *
91  **********************************************************************/
92 
93 ScrollView* image_win;
94 VariablesEditor* ve;
95 bool stillRunning = false;
96 
97 #ifdef __UNIX__
98 FILE *debug_window = NULL;       // opened on demand
99 #endif
100                                  // baseline norm words
101 ScrollView* bln_word_window = NULL;
102 
103 CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT;
104                                  // Selected words op
105 
106 BITS16 word_display_mode;
107 BOOL8 display_image = FALSE;
108 BOOL8 display_blocks = FALSE;
109 BOOL8 display_baselines = FALSE;
110 BOOL8 viewing_source = TRUE;
111 
112 BLOCK_LIST *source_block_list = NULL;    // image blocks
113 BLOCK_LIST target_block_list;    // target blocks
114 BLOCK_LIST *other_block_list = &target_block_list;
115 
116 BOOL8 source_changed = FALSE;    // Changes not saved
117 BOOL8 target_changed = FALSE;    // Changes not saved
118 BOOL8 *other_image_changed = &target_changed;
119 
120 
121 /* Public globals */
122 
123 #define EXTERN
124 
125 EXTERN BLOCK_LIST *current_block_list = NULL;
126 EXTERN BOOL8 *current_image_changed = &source_changed;
127 
128 /* Variables */
129 
130 EXTERN STRING_VAR(editor_image_win_name, "EditorImage",
131 "Editor image window name");
132 EXTERN INT_VAR(editor_image_xpos, 590, "Editor image X Pos");
133 EXTERN INT_VAR(editor_image_ypos, 10, "Editor image Y Pos");
134 EXTERN INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar");
135 EXTERN INT_VAR(editor_image_word_bb_color, ScrollView::BLUE,
136 "Word bounding box colour");
137 EXTERN INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW,
138 "Blob bounding box colour");
139 EXTERN INT_VAR(editor_image_text_color, ScrollView::WHITE,
140 "Correct text colour");
141 
142 EXTERN STRING_VAR(editor_dbwin_name, "EditorDBWin",
143 "Editor debug window name");
144 EXTERN INT_VAR(editor_dbwin_xpos, 50, "Editor debug window X Pos");
145 EXTERN INT_VAR(editor_dbwin_ypos, 500, "Editor debug window Y Pos");
146 EXTERN INT_VAR(editor_dbwin_height, 24, "Editor debug window height");
147 EXTERN INT_VAR(editor_dbwin_width, 80, "Editor debug window width");
148 
149 EXTERN STRING_VAR(editor_word_name, "BlnWords", "BL normalised word window");
150 EXTERN INT_VAR(editor_word_xpos, 60, "Word window X Pos");
151 EXTERN INT_VAR(editor_word_ypos, 510, "Word window Y Pos");
152 EXTERN INT_VAR(editor_word_height, 240, "Word window height");
153 EXTERN INT_VAR(editor_word_width, 655, "Word window width");
154 
155 EXTERN double_VAR(editor_smd_scale_factor, 1.0, "Scaling for smd image");
156 
157 /**********************************************************************
158  *  add_word()
159  *
160  *  Inserts the a word into a specified block list. The list is searched for a
161  *  block and row of the same file as those of the word to be added, which
162  *  contain the bounding box of the word.  If such a row is found, the new
163  *  word is inserted into the row in the correct X order.  If the
164  *  block is found, but not the row, a copy of the word's old row is added to
165  *  the block in the correct Y order, and the word is put in that row.
166  *  If neither the row nor the block are found, then the word's old block is
167  *  copied with only the word's row. It is added to the block list in the
168  *  correct Y order.
169  **********************************************************************/
170 
add_word(WERD * word,ROW * src_row,BLOCK * src_block,BLOCK_LIST * dest_block_list)171 void add_word(                            // to block list
172               WERD *word,                  // word to be added
173               ROW *src_row,                // source row
174               BLOCK *src_block,            // source block
175               BLOCK_LIST *dest_block_list  // add to this
176              ) {
177   BLOCK_IT block_it(dest_block_list);
178   BLOCK *block;                  // current block
179   BLOCK *dest_block = NULL;      // destination block
180   ROW_IT row_it;
181   ROW *row;                      // destination row
182   ROW *dest_row = NULL;          // destination row
183   WERD_IT word_it;
184   TBOX word_box = word->bounding_box();
185   TBOX insert_point_word_box;
186   BOOL8 seen_blocks_for_current_file = FALSE;
187 
188   block_it.mark_cycle_pt();
189   while(!block_it.cycled_list() &&(dest_block == NULL)) {
190     block = block_it.data();
191     if ((block->bounding_box().contains(word_box)) &&
192    (strcmp(block->name(), src_block->name()) == 0)) {
193       dest_block = block;        // found dest block
194       row_it.set_to_list(block->row_list());
195       row_it.mark_cycle_pt();
196       while((!row_it.cycled_list()) &&(dest_row == NULL)) {
197         row = row_it.data();
198         if (row->bounding_box().contains(word_box))
199           dest_row = row;        // found dest row
200         else
201           row_it.forward();
202       }
203     }
204     else
205       block_it.forward();
206   }
207 
208   if (dest_block == NULL) {      // make a new one
209     dest_block = new BLOCK;
210     *dest_block = *src_block;
211 
212     block_it.set_to_list(dest_block_list);
213     for (block_it.mark_cycle_pt();
214     !block_it.cycled_list(); block_it.forward()) {
215       block = block_it.data();
216 
217       if (!seen_blocks_for_current_file &&
218        (strcmp(block->name(), dest_block->name()) == 0))
219         seen_blocks_for_current_file = TRUE;
220 
221       if (seen_blocks_for_current_file &&
222        ((strcmp(block->name(), dest_block->name()) != 0) ||
223        (block->bounding_box().top() <
224         dest_block->bounding_box().top())))
225         break;
226     }
227 
228     if (block_it.cycled_list())
229                                  // didn't find insrt pt
230       block_it.add_to_end(dest_block);
231     else
232                                  // did find insert pt
233       block_it.add_before_stay_put(dest_block);
234   }
235 
236   if (dest_row == NULL) {        // make a new one
237     dest_row = new ROW;
238     *dest_row = *src_row;
239 
240     row_it.set_to_list(dest_block->row_list());
241     for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
242       if (row_it.data()->bounding_box().top() <
243         dest_row->bounding_box().top())
244         break;
245     }
246 
247     if (row_it.cycled_list())
248                                  // didn't find insrt pt
249         row_it.add_to_end(dest_row);
250     else
251                                  // did find insert pt
252       row_it.add_before_stay_put(dest_row);
253   }
254 
255   /* dest_block and dest_row are now found or built and inserted as necessesary
256     so add the word to dest row */
257 
258   word_it.set_to_list(dest_row->word_list());
259   for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) {
260     if (word_it.data()->bounding_box().right() >= word_box.left())
261       break;
262   }
263 
264   if (word_it.cycled_list())
265     word_it.add_to_end(word);   // didn't find insrt pt
266   else {                         // did find insert pt
267     insert_point_word_box = word_it.data()->bounding_box();
268     if (insert_point_word_box.contains(word_box) ||
269       word_box.contains(insert_point_word_box))
270       image_win->AddMessage("Refusing to add words which obliterate,"
271                                " or are obliterated by, others");
272     else {
273       if (word_it.data()->bounding_box().left() >
274         word->bounding_box().left())
275                                  // infront of insert pt
276         word_it.add_before_stay_put(word);
277       else
278                                  // behind insert pt
279         word_it.add_after_stay_put(word);
280     }
281   }
282 }
283 
284 
285 /**********************************************************************
286  *  bln_word_window_handle()
287  *
288  *  Return a WINDOW for the word window, creating it if necessary
289  **********************************************************************/
290 
291 class BlnEventHandler : public SVEventHandler {
292  public:
Notify(const SVEvent * sv_event)293   void Notify(const SVEvent* sv_event) {
294     if (sv_event->type == SVET_DESTROY)
295       bln_word_window = NULL;
296     else if (sv_event->type == SVET_CLICK)
297       show_point(current_block_list, sv_event->x, sv_event->y);
298   }
299 };
300 
bln_word_window_handle()301 ScrollView* bln_word_window_handle() {  // return handle
302                                  // not opened yet
303   if (bln_word_window == NULL) {
304     pgeditor_msg("Creating BLN word window...");
305     bln_word_window = new ScrollView(editor_word_name.string(),
306       editor_word_xpos, editor_word_ypos, editor_word_width,
307       editor_word_height, 4000, 4000, true);
308     BlnEventHandler* a = new BlnEventHandler();
309     bln_word_window->AddEventHandler(a);
310     pgeditor_msg("Creating BLN word window...Done");
311   }
312   return bln_word_window;
313 }
314 
315 /**********************************************************************
316  *  build_image_window()
317  *
318  *  Destroy the existing image window if there is one.  Work out how big the
319  *  new window needs to be. Create it and re-display.
320  **********************************************************************/
321 
build_image_window(TBOX page_bounding_box)322 void build_image_window(TBOX page_bounding_box) {
323   if (image_win != NULL) { delete image_win; }
324   image_win = new ScrollView(editor_image_win_name.string(),
325                              editor_image_xpos, editor_image_ypos,
326                              page_bounding_box.right() + 1,
327                              page_bounding_box.top() +
328                                editor_image_menuheight + 1,
329                              page_bounding_box.right() + 1,
330                              page_bounding_box.top() + 1,
331                              true);
332 }
333 
334 
335 /**********************************************************************
336  *  build_menu()
337  *
338  *  Construct the menu tree used by the command window
339  **********************************************************************/
340 namespace tesseract {
build_menu_new()341 SVMenuNode *Tesseract::build_menu_new() {
342 
343   SVMenuNode* parent_menu;
344   SVMenuNode* root_menu_item = new SVMenuNode();
345 
346   SVMenuNode* modes_menu_item = root_menu_item->AddChild("MODES");
347 
348   modes_menu_item->AddChild("Change Display",
349       CHANGE_DISP_CMD_EVENT);
350   modes_menu_item->AddChild("Delete",
351       DELETE_CMD_EVENT);
352   modes_menu_item->AddChild("Copy to TARGET",
353       COPY_CMD_EVENT);
354   modes_menu_item->AddChild("Change Text",
355       CHANGE_TEXT_CMD_EVENT);
356   modes_menu_item->AddChild("Toggle Correct Seg Flg",
357       TOGGLE_SEG_CMD_EVENT);
358   modes_menu_item->AddChild("Dump Word",
359       DUMP_WERD_CMD_EVENT);
360   modes_menu_item->AddChild("Show Point",
361       SHOW_POINT_CMD_EVENT);
362   modes_menu_item->AddChild("Row gaps hist",
363       ROW_SPACE_STAT_CMD_EVENT);
364   modes_menu_item->AddChild("Block gaps hist",
365       BLOCK_SPACE_STAT_CMD_EVENT);
366   modes_menu_item->AddChild("Show BL Norm Word",
367       SHOW_BLN_WERD_CMD_EVENT);
368   modes_menu_item->AddChild("Re-Segment Word",
369       SEGMENT_WERD_CMD_EVENT);
370   modes_menu_item->AddChild("Recog Words",
371       RECOG_WERDS);
372   modes_menu_item->AddChild("Recog Blobs",
373       RECOG_PSEUDO);
374 
375   parent_menu = root_menu_item->AddChild("DISPLAY");
376 
377   parent_menu->AddChild("Bounding Boxes",
378       BOUNDING_BOX_CMD_EVENT, FALSE);
379   parent_menu->AddChild("Correct Text",
380       CORRECT_TEXT_CMD_EVENT, FALSE);
381   parent_menu->AddChild("Polygonal Approx",
382       POLYGONAL_CMD_EVENT, FALSE);
383   parent_menu->AddChild("Baseline Normalised",
384       BL_NORM_CMD_EVENT, FALSE);
385   parent_menu->AddChild("Edge Steps",
386       BITMAP_CMD_EVENT, TRUE);
387 
388   parent_menu = root_menu_item->AddChild("OTHER");
389 
390   parent_menu->AddChild("Quit",
391       QUIT_CMD_EVENT);
392   parent_menu->AddChild("Tidy Target",
393       TIDY_CMD_EVENT);
394 
395   parent_menu->AddChild("View TARGET",
396       VIEW_CMD_EVENT, FALSE);
397   parent_menu->AddChild("Show Image",
398       IMAGE_CMD_EVENT, FALSE);
399   parent_menu->AddChild("ShowBlock Outlines",
400       BLOCKS_CMD_EVENT, FALSE);
401   parent_menu->AddChild("Show Baselines",
402       BASELINES_CMD_EVENT, FALSE);
403   parent_menu->AddChild("Write File",
404       WRITE_CMD_EVENT,    imagebasename.string());
405   parent_menu->AddChild("New Source File",
406       NEW_SOURCE_CMD_EVENT,    imagebasename.string());
407   parent_menu->AddChild("Uniform Display",
408       UNIFORM_DISP_CMD_EVENT);
409   parent_menu->AddChild("Refresh Display",
410       REFRESH_CMD_EVENT);
411 
412   return root_menu_item;
413 }
414 
415 }  // namespace tesseract
416 
417 
418 /**********************************************************************
419  *  display_bln_lines()
420  *
421  *  Display normalised baseline, x-height, ascender limit and descender limit
422  **********************************************************************/
423 
display_bln_lines(ScrollView * window,ScrollView::Color colour,float scale_factor,float y_offset,float minx,float maxx)424 void display_bln_lines(ScrollView* window,
425                        ScrollView::Color colour,
426                        float scale_factor,
427                        float y_offset,
428                        float minx,
429                        float maxx) {
430   window->Pen(colour);
431   window->Line(minx, y_offset + scale_factor * DESC_HEIGHT,
432                maxx, y_offset + scale_factor * DESC_HEIGHT);
433   window->Line(minx, y_offset + scale_factor * BL_HEIGHT,
434                maxx, y_offset + scale_factor * BL_HEIGHT);
435   window->Line(minx, y_offset + scale_factor * X_HEIGHT,
436                maxx, y_offset + scale_factor * X_HEIGHT);
437   window->Line(minx, y_offset + scale_factor * ASC_HEIGHT,
438                maxx, y_offset + scale_factor * ASC_HEIGHT);
439 }
440 
441 
442 /**********************************************************************
443  *  do_new_source()
444  *
445  *  Change to another source file.  Automatically tidy page first
446  *
447  **********************************************************************/
448 namespace tesseract {
do_new_source()449 void Tesseract::do_new_source(           // serialise
450                   ) {
451   FILE *infp;                    // input file
452 
453   char* name = image_win->ShowInputDialog("New Source File name");
454 
455   STRING name_str(name);
456   delete[] name;
457 
458   if (source_changed) {
459 
460     int a = image_win->ShowYesNoDialog(
461         "Source changes will be LOST.  Continue?(Y/N)");
462 	if (a != 'y') { image_win->AddMessage("Write cancelled"); return; }
463 
464   }
465 
466                                  // if not file exists
467   if (!(infp = fopen(name_str.string(), "r"))) {
468 
469      image_win->AddMessage("Cant open file " "%s" "", name_str.string());
470     return;
471   }
472 
473   fclose(infp);
474 
475   image_win->AddMessage("Reading file " "%s" "...", name_str.string());
476   source_block_list->clear();
477                                  // appends to SOURCE
478   pgeditor_read_file(name_str, source_block_list);
479   source_changed = FALSE;
480 
481   image_win->AddMessage("Doing automatic Tidy Target...");
482   viewing_source = FALSE;        // Force viewing source
483   do_tidy_cmd();
484 
485   image_win->AddMessage("Doing automatic Tidy Target...Done");
486 
487 }
488 }  // namespace tesseract
489 
490 
491 /**********************************************************************
492  *  do_re_display()
493  *
494  *  Redisplay page
495  **********************************************************************/
496 
497 void
498                                  // function to call
do_re_display(BOOL8 word_painter (BLOCK *,ROW *,WERD *))499 do_re_display(BOOL8 word_painter(
500 BLOCK *, ROW *, WERD *)) {
501   BLOCK_IT block_it(current_block_list);
502   BLOCK *block;
503   int block_count = 1;
504 
505   ROW_IT row_it;
506   ROW *row;
507 
508   WERD_IT word_it;
509   WERD *word;
510 
511   image_win->Clear();
512   if (display_image != 0) {
513     sv_show_sub_image(&page_image, 0, 0,
514       page_image.get_xsize(), page_image.get_ysize(),
515       image_win, 0, 0);
516   }
517 
518   for (block_it.mark_cycle_pt();
519   !block_it.cycled_list(); block_it.forward()) {
520     block = block_it.data();
521     row_it.set_to_list(block->row_list());
522     for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
523       row = row_it.data();
524       word_it.set_to_list(row->word_list());
525       for (word_it.mark_cycle_pt();
526       !word_it.cycled_list(); word_it.forward()) {
527         word = word_it.data();
528         word_painter(block, row, word);
529       }
530       if (display_baselines)
531         row->plot_baseline(image_win, ScrollView::GREEN);
532     }
533     if (display_blocks)
534       block->plot(image_win, block_count++, ScrollView::RED);
535   }
536   image_win->Update();
537 }
538 
539 
540 /**********************************************************************
541  *  do_tidy_cmd()
542  *
543  *  Tidy TARGET page
544  **********************************************************************/
545 
do_tidy_cmd()546 const TBOX do_tidy_cmd() {  // tidy
547   ICOORD shift_vector;
548   TBOX tidy_box;                  // Just the tidy area
549   TBOX source_box;                // source file area
550 
551   source_box = block_list_bounding_box(source_block_list);
552   // find src area
553 
554   if (!target_block_list.empty()) {
555     tidy_box = block_list_compress(&target_block_list);
556 
557     /* Shift tidied target above the source image area. */
558 
559     shift_vector = ICOORD(0, source_box.top() + BLOCK_SPACING)
560       - tidy_box.botleft();
561     block_list_move(&target_block_list, shift_vector);
562     tidy_box.move(shift_vector);
563   }
564   source_box += tidy_box;
565                                  // big enough for both
566   build_image_window(source_box);
567   do_view_cmd();
568   return tidy_box;
569 }
570 
571 
572 /**********************************************************************
573  *  do_view_cmd()
574  *
575  *  View TARGET/View SOURCE command
576  **********************************************************************/
577 
do_view_cmd()578 void do_view_cmd() {
579   viewing_source = !viewing_source;
580   image_win->Clear();
581   if (viewing_source) {
582     current_block_list = source_block_list;
583     current_image_changed = &source_changed;
584     other_block_list = &target_block_list;
585     other_image_changed = &target_changed;
586     do_re_display(&word_display);
587   }
588   else {
589     current_block_list = &target_block_list;
590     current_image_changed = &target_changed;
591     other_block_list = source_block_list;
592     other_image_changed = &source_changed;
593     do_re_display(&word_display);
594   }
595 }
596 
597 
598 /**********************************************************************
599  *  do_write_file()
600  *
601  *  Serialise a block list to file
602  *
603  *  If writing image, tidy page and move to(0,0) first
604  **********************************************************************/
605 
do_write_file()606 void do_write_file(           // serialise
607                   ) {
608 
609   char* name = image_win->ShowInputDialog("File Name");
610 
611   FILE *infp;                    // input file
612   char msg_str[80];
613 
614   TBOX enclosing_box;
615 
616                                  // if file exists
617   if ((infp = fopen(name, "r")) != NULL) {
618     fclose(infp);
619     sprintf(msg_str, "Overwrite file " "%s" "?(Y/N)", name);
620 
621     int a = image_win->ShowYesNoDialog(msg_str);
622 	if (a != 'y') { image_win->AddMessage("Write cancelled"); delete[] name; return; }
623   }
624 
625   infp = fopen(name, "w");      // can we write to it?
626   if (infp == NULL) {
627 
628   image_win->AddMessage("Cant write to file " "%s" "", name);
629     delete[] name;
630     return;
631   }
632   fclose(infp);
633 
634   delete [] name;
635 
636   if (!viewing_source && !target_block_list.empty()) {
637                                  // Tidy & move to(0,0)
638     image_win->AddMessage("Automatic tidy...");
639     viewing_source = TRUE;       // Stay viewing target!
640     enclosing_box = do_tidy_cmd();
641     block_list_move(&target_block_list, -enclosing_box.botleft());
642     image_win->AddMessage("Writing file...");
643     pgeditor_write_file(name, &target_block_list);
644                                  // move back
645     block_list_move(&target_block_list,
646       enclosing_box.botleft());
647   }
648   else {
649     image_win->AddMessage("Writing file...");
650     pgeditor_write_file(name, current_block_list);
651   }
652   image_win->AddMessage("Writing file...Done");
653   *current_image_changed = FALSE;
654 
655 }
656 
657 /**********************************************************************
658  *  notify()
659  *
660  *  Event handler that processes incoming events, either forwarding
661  *  them to process_cmd_win_event or process_image_event.
662  *
663  **********************************************************************/
664 
Notify(const SVEvent * event)665 void PGEventHandler::Notify(const SVEvent* event) {
666   char myval = '0';
667   if (event->type == SVET_POPUP) {
668 ve->Notify(event);
669   } // These are handled by Var. Editor
670   else if (event->type == SVET_EXIT) { stillRunning = false; }
671   else if (event->type == SVET_MENU) {
672      if (strcmp(event->parameter, "true") == 0) { myval = 'T'; }
673      else if (strcmp(event->parameter, "false") == 0) { myval = 'F'; }
674      tess_->process_cmd_win_event(event->command_id, &myval);
675   }
676   else {
677      tess_->process_image_event(*event);
678      // else    pgeditor_show_point(*event);
679   }
680   current_word_quit.set_value(FALSE);
681   selection_quit.set_value(FALSE);
682                                  // replot all var wins
683 }
684 
685 
686 /**********************************************************************
687  *  pgeditor_main()
688  *
689  *  Top level editor operation:
690  *  Setup a new window and an according event handler
691  *
692  **********************************************************************/
693 
694 namespace tesseract {
pgeditor_main(BLOCK_LIST * blocks)695 void Tesseract::pgeditor_main(BLOCK_LIST *blocks) {
696 
697   source_block_list = blocks;
698   current_block_list = blocks;
699   if (current_block_list->empty())
700     return;
701 
702   stillRunning = true;
703 
704   build_image_window(block_list_bounding_box(source_block_list));
705   word_display_mode.turn_on_bit(DF_EDGE_STEP);
706   do_re_display(&word_set_display);
707 #ifndef GRAPHICS_DISABLED
708   ve = new VariablesEditor(this, image_win);
709 #endif
710   PGEventHandler pgEventHandler(this);
711 
712   image_win->AddEventHandler(&pgEventHandler);
713   image_win->AddMessageBox();
714 
715   SVMenuNode* svMenuRoot = build_menu_new();
716 
717   svMenuRoot->BuildMenu(image_win);
718   image_win->SetVisible(true);
719 
720   image_win->AwaitEvent(SVET_DESTROY);
721   image_win->AddEventHandler(NULL);
722 }
723 }  // namespace tesseract
724 
725 
726 /**********************************************************************
727  * pgeditor_msg()
728  *
729  * Display a message - in the command window if there is one, or to stdout
730  **********************************************************************/
731 
pgeditor_msg(const char * msg)732 void pgeditor_msg( // message display
733                   const char *msg) {
734     image_win->AddMessage(msg);
735 }
736 
737 
738 /**********************************************************************
739  * pgeditor_read_file()
740  *
741  * Deserialise source file
742  **********************************************************************/
743 
744 namespace tesseract {
pgeditor_read_file(STRING & filename,BLOCK_LIST * blocks)745 void Tesseract::pgeditor_read_file(                   // of serialised file
746                                    STRING &filename,
747                                    BLOCK_LIST *blocks  // block list to add to
748                                   ) {
749   STRING name = filename;        //truncated name
750   const char *lastdot;           //of name
751   TO_BLOCK_LIST land_blocks, port_blocks;
752   TBOX page_box;
753 
754   lastdot = strrchr (name.string (), '.');
755   if (lastdot != NULL)
756     name[lastdot-name.string()] = '\0';
757   if (!read_unlv_file(name, page_image.get_xsize(), page_image.get_ysize(),
758                      blocks))
759     FullPageBlock(page_image.get_xsize(), page_image.get_ysize(), blocks);
760   find_components(blocks, &land_blocks, &port_blocks, &page_box);
761   textord_page(page_box.topright(), blocks, &land_blocks, &port_blocks, this);
762 }
763 }  // namespace tesseract
764 
765 /**********************************************************************
766  * pgeditor_show_point()
767  *
768  * Display the coordinates of a point in the command window
769  **********************************************************************/
770 
pgeditor_show_point(SVEvent * event)771 void pgeditor_show_point( // display coords
772                          SVEvent *event) {
773   image_win->AddMessage("Pointing at(%d, %d)", event->x, event->y);
774 }
775 
776 
777 /**********************************************************************
778  *  pgeditor_write_file()
779  *
780  *  Serialise a block list to file
781  *
782  **********************************************************************/
783 
pgeditor_write_file(char * name,BLOCK_LIST * blocks)784 void pgeditor_write_file(                   // serialise
785                          char *name,         // file name
786                          BLOCK_LIST *blocks  // block list to write
787                         ) {
788   FILE *infp;                    // input file
789   BLOCK_IT block_it(blocks);  // block iterator
790   BLOCK *block;                  // current block
791   ROW_IT row_it;                 // row iterator
792 
793   infp = fopen(name, "w");      // create output file
794   if (infp == NULL)
795     CANTCREATEFILE.error("pgeditor_write_file", EXIT, name);
796 
797   for (block_it.mark_cycle_pt();
798   !block_it.cycled_list(); block_it.forward()) {
799     block = block_it.extract();
800 
801     row_it.set_to_list(block->row_list());
802     for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward())
803                                  // ensure correct
804       row_it.data()->recalc_bounding_box();
805 
806     block->serialise(infp);     // serialize     non-empty
807     block_it.add_after_then_move(block);
808   }
809   fclose(infp);
810 }
811 
812 
813 /**********************************************************************
814  *  process_cmd_win_event()
815  *
816  *  Process a command returned from the command window
817  * (Just call the appropriate command handler)
818  **********************************************************************/
819 
820 namespace tesseract {
process_cmd_win_event(inT32 cmd_event,char * new_value)821 BOOL8 Tesseract::process_cmd_win_event(                 // UI command semantics
822                                        inT32 cmd_event,  // which menu item?
823                                        char *new_value   // any prompt data
824                                       ) {
825   char msg[160];
826   BOOL8 exit = FALSE;
827 
828   switch(cmd_event) {
829     case NULL_CMD_EVENT:
830       break;
831 
832     case VIEW_CMD_EVENT:
833       do_view_cmd();
834       break;
835     case CHANGE_DISP_CMD_EVENT:
836     case DELETE_CMD_EVENT:
837     case CHANGE_TEXT_CMD_EVENT:
838     case TOGGLE_SEG_CMD_EVENT:
839     case DUMP_WERD_CMD_EVENT:
840     case SHOW_POINT_CMD_EVENT:
841     case ROW_SPACE_STAT_CMD_EVENT:
842     case BLOCK_SPACE_STAT_CMD_EVENT:
843     case SHOW_BLN_WERD_CMD_EVENT:
844     case SEGMENT_WERD_CMD_EVENT:
845       mode =(CMD_EVENTS) cmd_event;
846       break;
847     case COPY_CMD_EVENT:
848       mode =(CMD_EVENTS) cmd_event;
849       if (!viewing_source)
850         image_win->AddMessage("Can't COPY while viewing target!");
851       break;
852     case BOUNDING_BOX_CMD_EVENT:
853       if (new_value[0] == 'T')
854         word_display_mode.turn_on_bit(DF_BOX);
855       else
856         word_display_mode.turn_off_bit(DF_BOX);
857       mode = CHANGE_DISP_CMD_EVENT;
858       break;
859     case CORRECT_TEXT_CMD_EVENT:
860       if (new_value[0] == 'T')
861         word_display_mode.turn_on_bit(DF_TEXT);
862       else
863         word_display_mode.turn_off_bit(DF_TEXT);
864       mode = CHANGE_DISP_CMD_EVENT;
865       break;
866     case POLYGONAL_CMD_EVENT:
867       if (new_value[0] == 'T')
868         word_display_mode.turn_on_bit(DF_POLYGONAL);
869       else
870         word_display_mode.turn_off_bit(DF_POLYGONAL);
871       mode = CHANGE_DISP_CMD_EVENT;
872       break;
873     case BL_NORM_CMD_EVENT:
874       if (new_value[0] == 'T')
875         word_display_mode.turn_on_bit(DF_BN_POLYGONAL);
876       else
877         word_display_mode.turn_off_bit(DF_BN_POLYGONAL);
878       mode = CHANGE_DISP_CMD_EVENT;
879       break;
880     case BITMAP_CMD_EVENT:
881       if (new_value[0] == 'T')
882         word_display_mode.turn_on_bit(DF_EDGE_STEP);
883       else
884         word_display_mode.turn_off_bit(DF_EDGE_STEP);
885       mode = CHANGE_DISP_CMD_EVENT;
886       break;
887     case UNIFORM_DISP_CMD_EVENT:
888       do_re_display(&word_set_display);
889       *current_image_changed = TRUE;
890       break;
891     case WRITE_CMD_EVENT:
892       do_write_file();
893       break;
894     case TIDY_CMD_EVENT:
895       if (!target_block_list.empty()) {
896         viewing_source = TRUE;   // Force viewing target
897         do_tidy_cmd();
898       }
899       break;
900     case NEW_SOURCE_CMD_EVENT:
901       do_new_source();
902       break;
903     case IMAGE_CMD_EVENT:
904       display_image =(new_value[0] == 'T');
905       do_re_display(&word_display);
906       break;
907     case BLOCKS_CMD_EVENT:
908       display_blocks =(new_value[0] == 'T');
909       do_re_display(&word_display);
910       break;
911     case BASELINES_CMD_EVENT:
912       display_baselines =(new_value[0] == 'T');
913       do_re_display(&word_display);
914       break;
915     case REFRESH_CMD_EVENT:
916       do_re_display(&word_display);
917       break;
918     case QUIT_CMD_EVENT:
919       if (source_changed || target_changed) {
920         int a = image_win->ShowYesNoDialog(
921             "Changes not saved. Exit anyway?(Y/N)");
922 	if (a == 'y') { exit = TRUE; ScrollView::Exit(); }
923       }
924       else {
925         exit = TRUE;
926 	ScrollView::Exit();
927 	}
928       break;
929     case RECOG_WERDS:
930       mode = RECOG_WERDS;
931     break;
932     case RECOG_PSEUDO:
933       mode = RECOG_PSEUDO;
934     break;
935 
936     default:
937       sprintf(msg, "Unrecognised event " INT32FORMAT "(%s)",
938                cmd_event, new_value);
939       image_win->AddMessage(msg);
940     break;
941   }
942   return exit;
943 }
944 
945 
946 /**********************************************************************
947  * process_image_event()
948  *
949  * User has done something in the image window - mouse down or up.  Work out
950  * what it is and do something with it.
951  * If DOWN - just remember where it was.
952  * If UP - for each word in the selected area do the operation defined by
953  * the current mode.
954  **********************************************************************/
process_image_event(const SVEvent & event)955 void Tesseract::process_image_event( // action in image win
956                                     const SVEvent &event) {
957   static ICOORD down;
958   ICOORD up;
959   TBOX selection_box;
960   char msg[80];
961 
962   switch(event.type) {
963 
964     case SVET_SELECTION:
965       if (event.type == SVET_SELECTION) {
966 	down.set_x(event.x - event.x_size);
967         down.set_y(event.y + event.y_size);
968         if (mode == SHOW_POINT_CMD_EVENT)
969           show_point(current_block_list, event.x, event.y);
970       }
971 
972       up.set_x(event.x);
973       up.set_y(event.y);
974 
975       selection_box = TBOX(down, up);
976 
977       switch(mode) {
978         case CHANGE_DISP_CMD_EVENT:
979           ::process_selected_words(current_block_list,
980                                  selection_box,
981                                  &word_blank_and_set_display);
982           break;
983         case COPY_CMD_EVENT:
984           if (!viewing_source)
985             image_win->AddMessage("Can't COPY while viewing target!");
986           else
987             ::process_selected_words(current_block_list,
988                                    selection_box,
989                                    &word_copy);
990           break;
991         case DELETE_CMD_EVENT:
992           ::process_selected_words_it(current_block_list,
993                                     selection_box,
994                                     &word_delete);
995           break;
996         case CHANGE_TEXT_CMD_EVENT:
997           ::process_selected_words(current_block_list,
998                                  selection_box,
999                                  &word_change_text);
1000           break;
1001         case TOGGLE_SEG_CMD_EVENT:
1002           ::process_selected_words(current_block_list,
1003                                    selection_box,
1004                                    &word_toggle_seg);
1005           break;
1006         case DUMP_WERD_CMD_EVENT:
1007           ::process_selected_words(current_block_list,
1008                                    selection_box,
1009                                    &word_dumper);
1010           break;
1011         case SHOW_BLN_WERD_CMD_EVENT:
1012           ::process_selected_words(current_block_list,
1013                                    selection_box,
1014                                    &word_bln_display);
1015           break;
1016         case SEGMENT_WERD_CMD_EVENT:
1017           re_segment_word(current_block_list, selection_box);
1018           break;
1019         case ROW_SPACE_STAT_CMD_EVENT:
1020           row_space_stat(current_block_list, selection_box);
1021           break;
1022         case BLOCK_SPACE_STAT_CMD_EVENT:
1023           block_space_stat(current_block_list, selection_box);
1024           break;
1025         case SHOW_POINT_CMD_EVENT:
1026           break;                 // ignore up event
1027 
1028         case RECOG_WERDS:
1029           image_win->AddMessage("Recogging selected words");
1030           this->process_selected_words(current_block_list,
1031                                        selection_box,
1032                                        &Tesseract::recog_interactive);
1033           break;
1034         case RECOG_PSEUDO:
1035           image_win->AddMessage("Recogging selected blobs");
1036           recog_pseudo_word(current_block_list, selection_box);
1037           break;
1038 
1039         default:
1040           sprintf(msg, "Mode %d not yet implemented", mode);
1041           image_win->AddMessage(msg);
1042           break;
1043       }
1044     default:
1045       break;
1046   }
1047 }
1048 }  // namespace tesseract
1049 
1050 
1051 /**********************************************************************
1052  * re_scale_and_move_bln_word()
1053  *
1054  * Scale and move a bln word so that it fits in a specified bounding box.
1055  * Scale by width or height to generate the largest image
1056  **********************************************************************/
1057 
re_scale_and_move_bln_word(WERD * norm_word,const TBOX & box)1058 float re_scale_and_move_bln_word(                 // put bln word in       box
1059                                  WERD *norm_word,  // BL normalised word
1060                                  const TBOX &box    // destination box
1061                                 ) {
1062   TBOX norm_box = norm_word->bounding_box();
1063   float width_scale_factor;
1064   float height_scale_factor;
1065   float selected_scale_factor;
1066 
1067   width_scale_factor = box.width() /(float) norm_box.width();
1068   height_scale_factor = box.height() /(float) ASC_HEIGHT;
1069 
1070   if ((ASC_HEIGHT * width_scale_factor) <= box.height())
1071     selected_scale_factor = width_scale_factor;
1072   else
1073     selected_scale_factor = height_scale_factor;
1074 
1075   norm_word->scale(selected_scale_factor);
1076   norm_word->move(ICOORD((box.left() + box.width() / 2), box.bottom()));
1077   return selected_scale_factor;
1078 }
1079 
1080 
1081 /**********************************************************************
1082  * re_segment_word()
1083  *
1084  * If all selected blobs are in the same row, remove them from their current
1085  * word(s) and put them in a new word.  Insert the new word in the row at the
1086  * appropriate point. Delete any empty words.
1087  *
1088  **********************************************************************/
1089 
re_segment_word(BLOCK_LIST * block_list,TBOX & selection_box)1090 void re_segment_word(                        // break/join words
1091                      BLOCK_LIST *block_list,  // blocks to check
1092                      TBOX &selection_box) {
1093   BLOCK_IT block_it(block_list);
1094   BLOCK *block;
1095   BLOCK *block_to_process = NULL;
1096   ROW_IT row_it;
1097   ROW *row;
1098   ROW *row_to_process = NULL;
1099   WERD_IT word_it;
1100   WERD *word;
1101   WERD *new_word = NULL;
1102   BOOL8 polyg = false;
1103   PBLOB_IT blob_it;
1104   PBLOB_LIST dummy;  // Just to initialize new_blob_it.
1105   PBLOB_IT new_blob_it = &dummy;
1106   PBLOB *blob;
1107 
1108   /* Find row to process - error if selections from more than one row */
1109 
1110   for (block_it.mark_cycle_pt();
1111   !block_it.cycled_list(); block_it.forward()) {
1112     block = block_it.data();
1113     if (block->bounding_box().overlap(selection_box)) {
1114       row_it.set_to_list(block->row_list());
1115       for (row_it.mark_cycle_pt();
1116       !row_it.cycled_list(); row_it.forward()) {
1117         row = row_it.data();
1118         if (row->bounding_box().overlap(selection_box)) {
1119           if (row_to_process == NULL) {
1120             block_to_process = block;
1121             row_to_process = row;
1122           }
1123           else {
1124 	    	image_win->AddMessage("Cant resegment words "
1125                                      "in more than one row");
1126             return;
1127           }
1128         }
1129       }
1130     }
1131   }
1132   /* Continue with row_to_process */
1133 
1134   word_it.set_to_list(row_to_process->word_list());
1135   for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) {
1136     word = word_it.data();
1137     polyg = word->flag(W_POLYGON);
1138     if (word->bounding_box().overlap(selection_box)) {
1139       blob_it.set_to_list(word->gblob_list());
1140       for (blob_it.mark_cycle_pt();
1141       !blob_it.cycled_list(); blob_it.forward()) {
1142         blob = blob_it.data();
1143         if (gblob_bounding_box(blob, polyg).overlap(selection_box)) {
1144           if (new_word == NULL) {
1145             new_word = word->shallow_copy();
1146             new_blob_it.set_to_list(new_word->gblob_list());
1147           }
1148           new_blob_it.add_to_end(blob_it.extract());
1149           // move blob
1150         }
1151       }
1152       if (blob_it.empty()) {    // no blobs in word
1153                                  // so delete word
1154         delete word_it.extract();
1155       }
1156     }
1157   }
1158   if (new_word != NULL) {
1159     gblob_sort_list(new_word->gblob_list(), polyg);
1160     word_it.add_to_end(new_word);
1161     word_it.sort(word_comparator);
1162     row_to_process->bounding_box().plot(image_win,
1163       ScrollView::BLACK, ScrollView::BLACK);
1164     word_it.set_to_list(row_to_process->word_list());
1165     for (word_it.mark_cycle_pt();
1166       !word_it.cycled_list(); word_it.forward())
1167     word_display(block_to_process, row_to_process, word_it.data());
1168     *current_image_changed = TRUE;
1169   }
1170 }
1171 
1172 
block_space_stat(BLOCK_LIST * block_list,TBOX & selection_box)1173 void block_space_stat(                        // show space stats
1174                       BLOCK_LIST *block_list,  // blocks to check
1175                       TBOX &selection_box) {
1176   BLOCK_IT block_it(block_list);
1177   BLOCK *block;
1178   ROW_IT row_it;
1179   ROW *row;
1180   int block_idx = 0;
1181   STATS all_gap_stats(0, MAXSPACING);
1182   WERD_IT word_it;
1183   WERD *word;
1184   PBLOB_IT blob_it;
1185   PBLOB *blob;
1186   C_BLOB_IT cblob_it;
1187   C_BLOB *cblob;
1188   TBOX box;
1189   inT16 prev_box_right;
1190   inT16 gap_width;
1191   inT16 min_inter_word_gap;
1192   inT16 max_inter_char_gap;
1193 
1194   /* Find blocks to process */
1195 
1196   for (block_it.mark_cycle_pt();
1197   !block_it.cycled_list(); block_it.forward()) {
1198     block_idx++;
1199     block = block_it.data();
1200     if (block->bounding_box().overlap(selection_box)) {
1201       /* Process a block */
1202       tprintf("\nBlock %d\n", block_idx);
1203       min_inter_word_gap = 3000;
1204       max_inter_char_gap = 0;
1205       all_gap_stats.clear();
1206       row_it.set_to_list(block->row_list());
1207       for (row_it.mark_cycle_pt();
1208       !row_it.cycled_list(); row_it.forward()) {
1209         row = row_it.data();
1210         prev_box_right = -1;
1211         word_it.set_to_list(row->word_list());
1212         for (word_it.mark_cycle_pt();
1213         !word_it.cycled_list(); word_it.forward()) {
1214           word = word_it.data();
1215           if (word->flag(W_POLYGON)) {
1216             blob_it.set_to_list(word->blob_list());
1217             for (blob_it.mark_cycle_pt();
1218             !blob_it.cycled_list(); blob_it.forward()) {
1219               blob = blob_it.data();
1220               box = blob->bounding_box();
1221               if (prev_box_right > -1) {
1222                 gap_width = box.left() - prev_box_right;
1223                 all_gap_stats.add(gap_width, 1);
1224                 if (blob_it.at_first()) {
1225                   if (gap_width < min_inter_word_gap)
1226                     min_inter_word_gap = gap_width;
1227                 }
1228                 else {
1229                   if (gap_width > max_inter_char_gap)
1230                     max_inter_char_gap = gap_width;
1231                 }
1232               }
1233               prev_box_right = box.right();
1234             }
1235           }
1236           else {
1237             cblob_it.set_to_list(word->cblob_list());
1238             for (cblob_it.mark_cycle_pt();
1239             !cblob_it.cycled_list(); cblob_it.forward()) {
1240               cblob = cblob_it.data();
1241               box = cblob->bounding_box();
1242               if (prev_box_right > -1) {
1243                 gap_width = box.left() - prev_box_right;
1244                 all_gap_stats.add(gap_width, 1);
1245                 if (cblob_it.at_first()) {
1246                   if (gap_width < min_inter_word_gap)
1247                     min_inter_word_gap = gap_width;
1248                 }
1249                 else {
1250                   if (gap_width > max_inter_char_gap)
1251                     max_inter_char_gap = gap_width;
1252                 }
1253               }
1254               prev_box_right = box.right();
1255             }
1256           }
1257         }
1258       }
1259       tprintf("Max inter char gap = %d.\nMin inter word gap = %d.\n",
1260         max_inter_char_gap, min_inter_word_gap);
1261       all_gap_stats.short_print(NULL, TRUE);
1262       all_gap_stats.smooth(2);
1263       tprintf("SMOOTHED DATA...\n");
1264       all_gap_stats.short_print(NULL, TRUE);
1265     }
1266   }
1267 }
1268 
1269 
row_space_stat(BLOCK_LIST * block_list,TBOX & selection_box)1270 void row_space_stat(                        // show space stats
1271                     BLOCK_LIST *block_list,  // blocks to check
1272                     TBOX &selection_box) {
1273   BLOCK_IT block_it(block_list);
1274   BLOCK *block;
1275   ROW_IT row_it;
1276   ROW *row;
1277   int block_idx = 0;
1278   int row_idx;
1279   STATS all_gap_stats(0, MAXSPACING);
1280   WERD_IT word_it;
1281   WERD *word;
1282   PBLOB_IT blob_it;
1283   PBLOB *blob;
1284   C_BLOB_IT cblob_it;
1285   C_BLOB *cblob;
1286   TBOX box;
1287   inT16 prev_box_right;
1288   inT16 gap_width;
1289   inT16 min_inter_word_gap;
1290   inT16 max_inter_char_gap;
1291 
1292   /* Find rows to process */
1293 
1294   for (block_it.mark_cycle_pt();
1295   !block_it.cycled_list(); block_it.forward()) {
1296     block_idx++;
1297     block = block_it.data();
1298     if (block->bounding_box().overlap(selection_box)) {
1299       row_it.set_to_list(block->row_list());
1300       row_idx = 0;
1301       for (row_it.mark_cycle_pt();
1302       !row_it.cycled_list(); row_it.forward()) {
1303         row_idx++;
1304         row = row_it.data();
1305         if (row->bounding_box().overlap(selection_box)) {
1306           /* Process a row */
1307 
1308           tprintf("\nBlock %d Row %d\n", block_idx, row_idx);
1309           min_inter_word_gap = 3000;
1310           max_inter_char_gap = 0;
1311           prev_box_right = -1;
1312           all_gap_stats.clear();
1313           word_it.set_to_list(row->word_list());
1314           for (word_it.mark_cycle_pt();
1315           !word_it.cycled_list(); word_it.forward()) {
1316             word = word_it.data();
1317             if (word->flag(W_POLYGON)) {
1318               blob_it.set_to_list(word->blob_list());
1319               for (blob_it.mark_cycle_pt();
1320               !blob_it.cycled_list(); blob_it.forward()) {
1321                 blob = blob_it.data();
1322                 box = blob->bounding_box();
1323                 if (prev_box_right > -1) {
1324                   gap_width = box.left() - prev_box_right;
1325                   all_gap_stats.add(gap_width, 1);
1326                   if (blob_it.at_first()) {
1327                     if (gap_width < min_inter_word_gap)
1328                       min_inter_word_gap = gap_width;
1329                   }
1330                   else {
1331                     if (gap_width > max_inter_char_gap)
1332                       max_inter_char_gap = gap_width;
1333                   }
1334                 }
1335                 prev_box_right = box.right();
1336               }
1337             }
1338             else {
1339               cblob_it.set_to_list(word->cblob_list());
1340               for (cblob_it.mark_cycle_pt();
1341               !cblob_it.cycled_list(); cblob_it.forward()) {
1342                 cblob = cblob_it.data();
1343                 box = cblob->bounding_box();
1344                 if (prev_box_right > -1) {
1345                   gap_width = box.left() - prev_box_right;
1346                   all_gap_stats.add(gap_width, 1);
1347                   if (cblob_it.at_first()) {
1348                     if (gap_width < min_inter_word_gap)
1349                       min_inter_word_gap = gap_width;
1350                   }
1351                   else {
1352                     if (gap_width > max_inter_char_gap)
1353                       max_inter_char_gap = gap_width;
1354                   }
1355                 }
1356                 prev_box_right = box.right();
1357               }
1358             }
1359           }
1360           tprintf
1361            ("Max inter char gap = %d.\nMin inter word gap = %d.\n",
1362             max_inter_char_gap, min_inter_word_gap);
1363           all_gap_stats.short_print(NULL, TRUE);
1364           all_gap_stats.smooth(2);
1365           tprintf("SMOOTHED DATA...\n");
1366           all_gap_stats.short_print(NULL, TRUE);
1367         }
1368       }
1369     }
1370   }
1371 }
1372 
1373 
1374 /**********************************************************************
1375  * show_point()
1376  *
1377  * Show coords of point, blob bounding box, word bounding box and offset from
1378  * row baseline
1379  **********************************************************************/
1380 
show_point(BLOCK_LIST * block_list,float x,float y)1381 void show_point(                        // display posn of bloba word
1382                 BLOCK_LIST *block_list,  // blocks to check
1383                 float x,
1384                 float y) {
1385   FCOORD pt(x, y);
1386   TBOX box;
1387   BLOCK_IT block_it(block_list);
1388   BLOCK *block;
1389   ROW_IT row_it;
1390   ROW *row;
1391   WERD_IT word_it;
1392   WERD *word;
1393   PBLOB_IT blob_it;
1394   PBLOB *blob;
1395   C_BLOB_IT cblob_it;
1396   C_BLOB *cblob;
1397 
1398   char msg[160];
1399   char *msg_ptr = msg;
1400 
1401   msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y);
1402 
1403   for (block_it.mark_cycle_pt();
1404   !block_it.cycled_list(); block_it.forward()) {
1405     block = block_it.data();
1406     if (block->bounding_box().contains(pt)) {
1407       row_it.set_to_list(block->row_list());
1408       for (row_it.mark_cycle_pt();
1409       !row_it.cycled_list(); row_it.forward()) {
1410         row = row_it.data();
1411         if (row->bounding_box().contains(pt)) {
1412           msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ",
1413             row->base_line(x));
1414 
1415           word_it.set_to_list(row->word_list());
1416           for (word_it.mark_cycle_pt();
1417           !word_it.cycled_list(); word_it.forward()) {
1418             word = word_it.data();
1419             box = word->bounding_box();
1420             if (box.contains(pt)) {
1421               msg_ptr += sprintf(msg_ptr,
1422                 "Wd(%d, %d)/(%d, %d) ",
1423                 box.left(), box.bottom(),
1424                 box.right(), box.top());
1425 
1426               if (word->flag(W_POLYGON)) {
1427                 blob_it.set_to_list(word->blob_list());
1428                 for (blob_it.mark_cycle_pt();
1429                   !blob_it.cycled_list();
1430                 blob_it.forward()) {
1431                   blob = blob_it.data();
1432                   box = blob->bounding_box();
1433                   if (box.contains(pt)) {
1434                     msg_ptr += sprintf(msg_ptr,
1435                       "Blb(%d, %d)/(%d, %d) ",
1436                       box.left(),
1437                       box.bottom(),
1438                       box.right(),
1439                       box.top());
1440                   }
1441                 }
1442               }
1443               else {
1444                 cblob_it.set_to_list(word->cblob_list());
1445                 for (cblob_it.mark_cycle_pt();
1446                   !cblob_it.cycled_list();
1447                 cblob_it.forward()) {
1448                   cblob = cblob_it.data();
1449                   box = cblob->bounding_box();
1450                   if (box.contains(pt)) {
1451                     msg_ptr += sprintf(msg_ptr,
1452                       "CBlb(%d, %d)/(%d, %d) ",
1453                       box.left(),
1454                       box.bottom(),
1455                       box.right(),
1456                       box.top());
1457                   }
1458                 }
1459               }
1460             }
1461           }
1462         }
1463       }
1464     }
1465   }
1466   image_win->AddMessage(msg);
1467 }
1468 
1469 
1470 /**********************************************************************
1471  * WERD PROCESSOR FUNCTIONS
1472  * ========================
1473  *
1474  * These routines are invoked by one or mode of:
1475  *    process_all_words()
1476  *    process_selected_words()
1477  * or
1478  *    process_all_words_it()
1479  *    process_selected_words_it()
1480  * for each word to be processed
1481  **********************************************************************/
1482 
1483 /**********************************************************************
1484  * word_blank_and_set_display()  Word processor
1485  *
1486  * Blank display of word then redisplay word according to current display mode
1487  * settings
1488  **********************************************************************/
1489 
word_blank_and_set_display(BLOCK * block,ROW * row,WERD * word)1490 BOOL8 word_blank_and_set_display(              // display a word
1491                                  BLOCK *block,  // block holding word
1492                                  ROW *row,      // row holding word
1493                                  WERD *word     // word to be processed
1494                                 ) {
1495   word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
1496   return word_set_display(block, row, word);
1497 }
1498 
1499 
1500 /**********************************************************************
1501  * word_bln_display()
1502  *
1503  * Normalise word and display in word window
1504  **********************************************************************/
1505 
word_bln_display(BLOCK *,ROW * row,WERD * word)1506 BOOL8 word_bln_display(           // bln & display
1507                        BLOCK *,    // block holding word
1508                        ROW *row,   // row holding word
1509                        WERD *word  // word to be processed
1510                       ) {
1511   WERD *bln_word;
1512 
1513   bln_word = word->poly_copy(row->x_height());
1514   bln_word->baseline_normalise(row);
1515   bln_word_window_handle()->Clear();
1516   display_bln_lines(bln_word_window_handle(), ScrollView::CYAN,
1517                      1.0, 0.0f, -1000.0f, 1000.0f);
1518   bln_word->plot(bln_word_window_handle(), ScrollView::RED);
1519   delete bln_word;
1520   return TRUE;
1521 }
1522 
1523 
1524 /**********************************************************************
1525  * word_change_text()
1526  *
1527  * Change the correct text of a word
1528  **********************************************************************/
1529 
word_change_text(BLOCK * block,ROW * row,WERD * word)1530 BOOL8 word_change_text(              // change correct text
1531                        BLOCK *block,  // block holding word
1532                        ROW *row,      // row holding word
1533                        WERD *word     // word to be processed
1534                       ) {
1535   char* cp = image_win->ShowInputDialog(
1536       "Enter/edit the correct text and press <<RETURN>>");
1537   word->set_text(cp);
1538   delete[] cp;
1539 
1540   if (word_display_mode.bit(DF_TEXT) || word->display_flag(DF_TEXT)) {
1541     word_blank_and_set_display(block, row, word);
1542     ScrollView::Update();
1543   }
1544 
1545   *current_image_changed = TRUE;
1546   return TRUE;
1547 }
1548 
1549 
1550 /**********************************************************************
1551  * word_copy()
1552  *
1553  * Copy a word to other display list
1554  **********************************************************************/
1555 
word_copy(BLOCK * block,ROW * row,WERD * word)1556 BOOL8 word_copy(              // copy a word
1557                 BLOCK *block,  // block holding word
1558                 ROW *row,      // row holding word
1559                 WERD *word     // word to be processed
1560                ) {
1561   WERD *copy_word = new WERD;
1562 
1563   *copy_word = *word;
1564   add_word(copy_word, row, block, other_block_list);
1565   *other_image_changed = TRUE;
1566   return TRUE;
1567 }
1568 
1569 
1570 /**********************************************************************
1571  * word_delete()
1572  *
1573  * Delete a word
1574  **********************************************************************/
1575 
word_delete(BLOCK * block,ROW * row,WERD * word,BLOCK_IT & block_it,ROW_IT & row_it,WERD_IT & word_it)1576 BOOL8 word_delete(                    // delete a word
1577                   BLOCK *block,        // block holding word
1578                   ROW *row,            // row holding word
1579                   WERD *word,          // word to be processed
1580                   BLOCK_IT &block_it,  // block list iterator
1581                   ROW_IT &row_it,      // row list iterator
1582                   WERD_IT &word_it     // word list iterator
1583                  ) {
1584   word_it.extract();
1585   word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
1586   delete(word);
1587 
1588   if (word_it.empty()) {        // no words left in row
1589                                  // so delete row
1590     row_it.extract();
1591     row->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
1592     delete(row);
1593 
1594     if (row_it.empty()) {       // no rows left in blk
1595                                  // so delete block
1596       block_it.extract();
1597       block->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK);
1598       delete(block);
1599     }
1600   }
1601   *current_image_changed = TRUE;
1602   return TRUE;
1603 }
1604 
1605 
1606 /**********************************************************************
1607  *  word_display()  Word Processor
1608  *
1609  *  Display a word according to its display modes
1610  **********************************************************************/
1611 
word_display(BLOCK *,ROW * row,WERD * word)1612 BOOL8 word_display(           // display a word
1613                    BLOCK *,    // block holding word
1614                    ROW *row,   // row holding word
1615                    WERD *word  // word to be processed
1616                   ) {
1617   TBOX word_bb;                   // word bounding box
1618   int word_height;               // ht of word BB
1619   BOOL8 displayed_something = FALSE;
1620   BOOL8 displayed_rainbow = FALSE;
1621   float shift;                   // from bot left
1622   PBLOB_IT it;                   // blob iterator
1623   C_BLOB_IT c_it;                // cblob iterator
1624   WERD *word_ptr;                // poly copy
1625   WERD temp_word;
1626   float scale_factor;            // for BN_POLYGON
1627 
1628   /*
1629     Note the double coercions of(COLOUR)((inT32)editor_image_word_bb_color)
1630     etc. are to keep the compiler happy.
1631   */
1632 
1633                                  // display bounding box
1634   if (word->display_flag(DF_BOX)) {
1635     word->bounding_box().plot(image_win,
1636      (ScrollView::Color)((inT32)
1637       editor_image_word_bb_color),
1638      (ScrollView::Color)((inT32)
1639       editor_image_word_bb_color));
1640 
1641     ScrollView::Color c = (ScrollView::Color)
1642        ((inT32) editor_image_blob_bb_color);
1643     image_win->Pen(c);
1644     if (word->flag(W_POLYGON)) {
1645       it.set_to_list(word->blob_list());
1646       for (it.mark_cycle_pt(); !it.cycled_list(); it.forward())
1647         it.data()->bounding_box().plot(image_win);
1648     }
1649     else {
1650       c_it.set_to_list(word->cblob_list());
1651       for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward())
1652         c_it.data()->bounding_box().plot(image_win);
1653     }
1654     displayed_something = TRUE;
1655   }
1656 
1657                                  // display edge steps
1658   if (word->display_flag(DF_EDGE_STEP) &&
1659   !word->flag(W_POLYGON)) {     // edgesteps available
1660     word->plot(image_win);      // rainbow colors
1661     displayed_something = TRUE;
1662     displayed_rainbow = TRUE;
1663   }
1664 
1665                                  // display poly approx
1666   if (word->display_flag(DF_POLYGONAL)) {
1667                                  // need to convert
1668     if (!word->flag(W_POLYGON)) {
1669       word_ptr = word->poly_copy(row->x_height());
1670 
1671       /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */
1672 
1673       if (displayed_rainbow)
1674                                  // ensure its visible
1675         word_ptr->plot(image_win, ScrollView::WHITE);
1676       else
1677                                  // rainbow colors
1678           word_ptr->plot(image_win);
1679       delete word_ptr;
1680     }
1681     else {
1682       if (displayed_rainbow)
1683                                  // ensure its visible
1684         word->plot(image_win, ScrollView::WHITE);
1685       else
1686         word->plot(image_win);  // rainbow colors
1687     }
1688 
1689     displayed_rainbow = TRUE;
1690     displayed_something = TRUE;
1691   }
1692 
1693                                  // disp BN poly approx
1694   if (word->display_flag(DF_BN_POLYGONAL)) {
1695                                  // need to convert
1696     if (!word->flag(W_POLYGON)) {
1697       word_ptr = word->poly_copy(row->x_height());
1698       temp_word = *word_ptr;
1699       delete word_ptr;
1700 
1701       /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */
1702 
1703     }
1704     else
1705       temp_word = *word;         // copy word
1706     word_bb = word->bounding_box();
1707     if (!temp_word.flag(W_NORMALIZED))
1708       temp_word.baseline_normalise(row);
1709 
1710     scale_factor = re_scale_and_move_bln_word(&temp_word, word_bb);
1711     display_bln_lines(image_win, ScrollView::CYAN, scale_factor,
1712       word_bb.bottom(), word_bb.left(), word_bb.right());
1713 
1714     if (displayed_rainbow)
1715                                  // ensure its visible
1716       temp_word.plot(image_win, ScrollView::WHITE);
1717     else
1718       temp_word.plot(image_win); // rainbow colors
1719 
1720     displayed_rainbow = TRUE;
1721     displayed_something = TRUE;
1722   }
1723 
1724   // display correct text
1725   if (word->display_flag(DF_TEXT)) {
1726     word_bb = word->bounding_box();
1727     ScrollView::Color c =(ScrollView::Color)
1728        ((inT32) editor_image_blob_bb_color);
1729     image_win->Pen(c);
1730     word_height = word_bb.height();
1731     image_win->TextAttributes("Times", 0.75 * word_height,
1732                               false, false, false);
1733     if (word_height < word_bb.width())
1734       shift = 0.25 * word_height;
1735     else
1736       shift = 0.0f;
1737 
1738     image_win->Text(word_bb.left() + shift,
1739                     word_bb.bottom() + 0.25 * word_height, word->text());
1740 
1741     if (strlen(word->text()) > 0)
1742       displayed_something = TRUE;
1743   }
1744 
1745   if (!displayed_something)      // display BBox anyway
1746     word->bounding_box().plot(image_win,
1747      (ScrollView::Color)((inT32) editor_image_word_bb_color),
1748      (ScrollView::Color)((inT32)
1749       editor_image_word_bb_color));
1750   return TRUE;
1751 }
1752 
1753 
1754 /**********************************************************************
1755  * word_dumper()
1756  *
1757  * Dump members to the debug window
1758  **********************************************************************/
1759 
word_dumper(BLOCK * block,ROW * row,WERD * word)1760 BOOL8 word_dumper(              // dump word
1761                   BLOCK *block,  // block holding word
1762                   ROW *row,      // row holding word
1763                   WERD *word     // word to be processed
1764                  ) {
1765 
1766   if (block != NULL) {
1767   tprintf("\nBlock data...\n");
1768   block->print(NULL, FALSE);
1769   }
1770   tprintf("\nRow data...\n");
1771   row->print(NULL);
1772   tprintf("\nWord data...\n");
1773   word->print(NULL);
1774   return TRUE;
1775 }
1776 
1777 
1778 /**********************************************************************
1779  * word_set_display()  Word processor
1780  *
1781  * Display word according to current display mode settings
1782  **********************************************************************/
1783 
word_set_display(BLOCK * block,ROW * row,WERD * word)1784 BOOL8 word_set_display(              // display a word
1785                        BLOCK *block,  // block holding word
1786                        ROW *row,      // row holding word
1787                        WERD *word     // word to be processed
1788                       ) {
1789   TBOX word_bb;                   // word bounding box
1790 
1791   word->set_display_flag(DF_BOX, word_display_mode.bit(DF_BOX));
1792   word->set_display_flag(DF_TEXT, word_display_mode.bit(DF_TEXT));
1793   word->set_display_flag(DF_POLYGONAL, word_display_mode.bit(DF_POLYGONAL));
1794   word->set_display_flag(DF_EDGE_STEP, word_display_mode.bit(DF_EDGE_STEP));
1795   word->set_display_flag(DF_BN_POLYGONAL,
1796     word_display_mode.bit(DF_BN_POLYGONAL));
1797   *current_image_changed = TRUE;
1798   return word_display(block, row, word);
1799 }
1800 
1801 
1802 /**********************************************************************
1803  * word_toggle_seg()
1804  *
1805  * Toggle the correct segmentation flag
1806  **********************************************************************/
1807 
word_toggle_seg(BLOCK *,ROW *,WERD * word)1808 BOOL8 word_toggle_seg(           // toggle seg flag
1809                       BLOCK *,    // block holding word
1810                       ROW *,      // row holding word
1811                       WERD *word  // word to be processed
1812                      ) {
1813   word->set_flag(W_SEGMENTED, !word->flag(W_SEGMENTED));
1814   *current_image_changed = TRUE;
1815   return TRUE;
1816 }
1817 
1818 #endif  // GRAPHICS_DISABLED
1819 
1820 /* DEBUG ONLY */
1821 
do_check_mem(inT32 level)1822 void do_check_mem( // do it
1823                   inT32 level) {
1824   check_mem("Doing it", level);
1825 }
1826