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