| /********************************************************************** |
| * File: pgedit.cpp (Formerly pgeditor.c) |
| * Description: Page structure file editor |
| * Author: Phil Cheatle |
| * Created: Thu Oct 10 16:25:24 BST 1991 |
| * |
| *(C) Copyright 1991, Hewlett-Packard Ltd. |
| ** Licensed under the Apache License, Version 2.0(the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** http:// www.apache.org/licenses/LICENSE-2.0 |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| * |
| **********************************************************************/ |
| |
| #include "pgedit.h" |
| |
| #include <ctype.h> |
| #include <math.h> |
| |
| #include "genblob.h" |
| #include "tessio.h" |
| #include "tessout.h" |
| #include "tordmain.h" |
| #include "statistc.h" |
| #include "debugwin.h" |
| #include "svshowim.h" |
| #include "mainblk.h" |
| #include "varabled.h" |
| #include "string.h" |
| |
| #include "scrollview.h" |
| #include "svmnode.h" |
| |
| #include "control.h" |
| #include "tesseractclass.h" |
| |
| #include "blread.h" |
| |
| #ifndef GRAPHICS_DISABLED |
| #define ASC_HEIGHT (2 * bln_baseline_offset + bln_x_height) |
| #define X_HEIGHT (bln_baseline_offset + bln_x_height) |
| #define BL_HEIGHT bln_baseline_offset |
| #define DESC_HEIGHT 0 |
| #define MAXSPACING 128 /*max expected spacing in pix */ |
| |
| const ERRCODE EMPTYBLOCKLIST = "No blocks to edit"; |
| extern IMAGE page_image; |
| |
| enum CMD_EVENTS |
| { |
| NULL_CMD_EVENT, |
| DELETE_CMD_EVENT, |
| COPY_CMD_EVENT, |
| CHANGE_DISP_CMD_EVENT, |
| CHANGE_TEXT_CMD_EVENT, |
| TOGGLE_SEG_CMD_EVENT, |
| DUMP_WERD_CMD_EVENT, |
| SHOW_POINT_CMD_EVENT, |
| ROW_SPACE_STAT_CMD_EVENT, |
| BLOCK_SPACE_STAT_CMD_EVENT, |
| SHOW_BLN_WERD_CMD_EVENT, |
| SEGMENT_WERD_CMD_EVENT, |
| BOUNDING_BOX_CMD_EVENT, |
| CORRECT_TEXT_CMD_EVENT, |
| POLYGONAL_CMD_EVENT, |
| BL_NORM_CMD_EVENT, |
| BITMAP_CMD_EVENT, |
| TIDY_CMD_EVENT, |
| VIEW_CMD_EVENT, |
| IMAGE_CMD_EVENT, |
| BLOCKS_CMD_EVENT, |
| BASELINES_CMD_EVENT, |
| WRITE_CMD_EVENT, |
| NEW_SOURCE_CMD_EVENT, |
| UNIFORM_DISP_CMD_EVENT, |
| REFRESH_CMD_EVENT, |
| QUIT_CMD_EVENT, |
| RECOG_WERDS, |
| RECOG_PSEUDO |
| }; |
| |
| /********************************************************************** |
| * |
| * Some global data |
| * |
| **********************************************************************/ |
| |
| ScrollView* image_win; |
| VariablesEditor* ve; |
| bool stillRunning = false; |
| |
| #ifdef __UNIX__ |
| FILE *debug_window = NULL; // opened on demand |
| #endif |
| // baseline norm words |
| ScrollView* bln_word_window = NULL; |
| |
| CMD_EVENTS mode = CHANGE_DISP_CMD_EVENT; |
| // Selected words op |
| |
| BITS16 word_display_mode; |
| BOOL8 display_image = FALSE; |
| BOOL8 display_blocks = FALSE; |
| BOOL8 display_baselines = FALSE; |
| BOOL8 viewing_source = TRUE; |
| |
| BLOCK_LIST *source_block_list = NULL; // image blocks |
| BLOCK_LIST target_block_list; // target blocks |
| BLOCK_LIST *other_block_list = &target_block_list; |
| |
| BOOL8 source_changed = FALSE; // Changes not saved |
| BOOL8 target_changed = FALSE; // Changes not saved |
| BOOL8 *other_image_changed = &target_changed; |
| |
| |
| /* Public globals */ |
| |
| #define EXTERN |
| |
| EXTERN BLOCK_LIST *current_block_list = NULL; |
| EXTERN BOOL8 *current_image_changed = &source_changed; |
| |
| /* Variables */ |
| |
| EXTERN STRING_VAR(editor_image_win_name, "EditorImage", |
| "Editor image window name"); |
| EXTERN INT_VAR(editor_image_xpos, 590, "Editor image X Pos"); |
| EXTERN INT_VAR(editor_image_ypos, 10, "Editor image Y Pos"); |
| EXTERN INT_VAR(editor_image_menuheight, 50, "Add to image height for menu bar"); |
| EXTERN INT_VAR(editor_image_word_bb_color, ScrollView::BLUE, |
| "Word bounding box colour"); |
| EXTERN INT_VAR(editor_image_blob_bb_color, ScrollView::YELLOW, |
| "Blob bounding box colour"); |
| EXTERN INT_VAR(editor_image_text_color, ScrollView::WHITE, |
| "Correct text colour"); |
| |
| EXTERN STRING_VAR(editor_dbwin_name, "EditorDBWin", |
| "Editor debug window name"); |
| EXTERN INT_VAR(editor_dbwin_xpos, 50, "Editor debug window X Pos"); |
| EXTERN INT_VAR(editor_dbwin_ypos, 500, "Editor debug window Y Pos"); |
| EXTERN INT_VAR(editor_dbwin_height, 24, "Editor debug window height"); |
| EXTERN INT_VAR(editor_dbwin_width, 80, "Editor debug window width"); |
| |
| EXTERN STRING_VAR(editor_word_name, "BlnWords", "BL normalised word window"); |
| EXTERN INT_VAR(editor_word_xpos, 60, "Word window X Pos"); |
| EXTERN INT_VAR(editor_word_ypos, 510, "Word window Y Pos"); |
| EXTERN INT_VAR(editor_word_height, 240, "Word window height"); |
| EXTERN INT_VAR(editor_word_width, 655, "Word window width"); |
| |
| EXTERN double_VAR(editor_smd_scale_factor, 1.0, "Scaling for smd image"); |
| |
| /********************************************************************** |
| * add_word() |
| * |
| * Inserts the a word into a specified block list. The list is searched for a |
| * block and row of the same file as those of the word to be added, which |
| * contain the bounding box of the word. If such a row is found, the new |
| * word is inserted into the row in the correct X order. If the |
| * block is found, but not the row, a copy of the word's old row is added to |
| * the block in the correct Y order, and the word is put in that row. |
| * If neither the row nor the block are found, then the word's old block is |
| * copied with only the word's row. It is added to the block list in the |
| * correct Y order. |
| **********************************************************************/ |
| |
| void add_word( // to block list |
| WERD *word, // word to be added |
| ROW *src_row, // source row |
| BLOCK *src_block, // source block |
| BLOCK_LIST *dest_block_list // add to this |
| ) { |
| BLOCK_IT block_it(dest_block_list); |
| BLOCK *block; // current block |
| BLOCK *dest_block = NULL; // destination block |
| ROW_IT row_it; |
| ROW *row; // destination row |
| ROW *dest_row = NULL; // destination row |
| WERD_IT word_it; |
| TBOX word_box = word->bounding_box(); |
| TBOX insert_point_word_box; |
| BOOL8 seen_blocks_for_current_file = FALSE; |
| |
| block_it.mark_cycle_pt(); |
| while(!block_it.cycled_list() &&(dest_block == NULL)) { |
| block = block_it.data(); |
| if ((block->bounding_box().contains(word_box)) && |
| (strcmp(block->name(), src_block->name()) == 0)) { |
| dest_block = block; // found dest block |
| row_it.set_to_list(block->row_list()); |
| row_it.mark_cycle_pt(); |
| while((!row_it.cycled_list()) &&(dest_row == NULL)) { |
| row = row_it.data(); |
| if (row->bounding_box().contains(word_box)) |
| dest_row = row; // found dest row |
| else |
| row_it.forward(); |
| } |
| } |
| else |
| block_it.forward(); |
| } |
| |
| if (dest_block == NULL) { // make a new one |
| dest_block = new BLOCK; |
| *dest_block = *src_block; |
| |
| block_it.set_to_list(dest_block_list); |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block = block_it.data(); |
| |
| if (!seen_blocks_for_current_file && |
| (strcmp(block->name(), dest_block->name()) == 0)) |
| seen_blocks_for_current_file = TRUE; |
| |
| if (seen_blocks_for_current_file && |
| ((strcmp(block->name(), dest_block->name()) != 0) || |
| (block->bounding_box().top() < |
| dest_block->bounding_box().top()))) |
| break; |
| } |
| |
| if (block_it.cycled_list()) |
| // didn't find insrt pt |
| block_it.add_to_end(dest_block); |
| else |
| // did find insert pt |
| block_it.add_before_stay_put(dest_block); |
| } |
| |
| if (dest_row == NULL) { // make a new one |
| dest_row = new ROW; |
| *dest_row = *src_row; |
| |
| row_it.set_to_list(dest_block->row_list()); |
| for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { |
| if (row_it.data()->bounding_box().top() < |
| dest_row->bounding_box().top()) |
| break; |
| } |
| |
| if (row_it.cycled_list()) |
| // didn't find insrt pt |
| row_it.add_to_end(dest_row); |
| else |
| // did find insert pt |
| row_it.add_before_stay_put(dest_row); |
| } |
| |
| /* dest_block and dest_row are now found or built and inserted as necessesary |
| so add the word to dest row */ |
| |
| word_it.set_to_list(dest_row->word_list()); |
| for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { |
| if (word_it.data()->bounding_box().right() >= word_box.left()) |
| break; |
| } |
| |
| if (word_it.cycled_list()) |
| word_it.add_to_end(word); // didn't find insrt pt |
| else { // did find insert pt |
| insert_point_word_box = word_it.data()->bounding_box(); |
| if (insert_point_word_box.contains(word_box) || |
| word_box.contains(insert_point_word_box)) |
| image_win->AddMessage("Refusing to add words which obliterate," |
| " or are obliterated by, others"); |
| else { |
| if (word_it.data()->bounding_box().left() > |
| word->bounding_box().left()) |
| // infront of insert pt |
| word_it.add_before_stay_put(word); |
| else |
| // behind insert pt |
| word_it.add_after_stay_put(word); |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * bln_word_window_handle() |
| * |
| * Return a WINDOW for the word window, creating it if necessary |
| **********************************************************************/ |
| |
| class BlnEventHandler : public SVEventHandler { |
| public: |
| void Notify(const SVEvent* sv_event) { |
| if (sv_event->type == SVET_DESTROY) |
| bln_word_window = NULL; |
| else if (sv_event->type == SVET_CLICK) |
| show_point(current_block_list, sv_event->x, sv_event->y); |
| } |
| }; |
| |
| ScrollView* bln_word_window_handle() { // return handle |
| // not opened yet |
| if (bln_word_window == NULL) { |
| pgeditor_msg("Creating BLN word window..."); |
| bln_word_window = new ScrollView(editor_word_name.string(), |
| editor_word_xpos, editor_word_ypos, editor_word_width, |
| editor_word_height, 4000, 4000, true); |
| BlnEventHandler* a = new BlnEventHandler(); |
| bln_word_window->AddEventHandler(a); |
| pgeditor_msg("Creating BLN word window...Done"); |
| } |
| return bln_word_window; |
| } |
| |
| /********************************************************************** |
| * build_image_window() |
| * |
| * Destroy the existing image window if there is one. Work out how big the |
| * new window needs to be. Create it and re-display. |
| **********************************************************************/ |
| |
| void build_image_window(TBOX page_bounding_box) { |
| if (image_win != NULL) { delete image_win; } |
| image_win = new ScrollView(editor_image_win_name.string(), |
| editor_image_xpos, editor_image_ypos, |
| page_bounding_box.right() + 1, |
| page_bounding_box.top() + |
| editor_image_menuheight + 1, |
| page_bounding_box.right() + 1, |
| page_bounding_box.top() + 1, |
| true); |
| } |
| |
| |
| /********************************************************************** |
| * build_menu() |
| * |
| * Construct the menu tree used by the command window |
| **********************************************************************/ |
| namespace tesseract { |
| SVMenuNode *Tesseract::build_menu_new() { |
| |
| SVMenuNode* parent_menu; |
| SVMenuNode* root_menu_item = new SVMenuNode(); |
| |
| SVMenuNode* modes_menu_item = root_menu_item->AddChild("MODES"); |
| |
| modes_menu_item->AddChild("Change Display", |
| CHANGE_DISP_CMD_EVENT); |
| modes_menu_item->AddChild("Delete", |
| DELETE_CMD_EVENT); |
| modes_menu_item->AddChild("Copy to TARGET", |
| COPY_CMD_EVENT); |
| modes_menu_item->AddChild("Change Text", |
| CHANGE_TEXT_CMD_EVENT); |
| modes_menu_item->AddChild("Toggle Correct Seg Flg", |
| TOGGLE_SEG_CMD_EVENT); |
| modes_menu_item->AddChild("Dump Word", |
| DUMP_WERD_CMD_EVENT); |
| modes_menu_item->AddChild("Show Point", |
| SHOW_POINT_CMD_EVENT); |
| modes_menu_item->AddChild("Row gaps hist", |
| ROW_SPACE_STAT_CMD_EVENT); |
| modes_menu_item->AddChild("Block gaps hist", |
| BLOCK_SPACE_STAT_CMD_EVENT); |
| modes_menu_item->AddChild("Show BL Norm Word", |
| SHOW_BLN_WERD_CMD_EVENT); |
| modes_menu_item->AddChild("Re-Segment Word", |
| SEGMENT_WERD_CMD_EVENT); |
| modes_menu_item->AddChild("Recog Words", |
| RECOG_WERDS); |
| modes_menu_item->AddChild("Recog Blobs", |
| RECOG_PSEUDO); |
| |
| parent_menu = root_menu_item->AddChild("DISPLAY"); |
| |
| parent_menu->AddChild("Bounding Boxes", |
| BOUNDING_BOX_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Correct Text", |
| CORRECT_TEXT_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Polygonal Approx", |
| POLYGONAL_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Baseline Normalised", |
| BL_NORM_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Edge Steps", |
| BITMAP_CMD_EVENT, TRUE); |
| |
| parent_menu = root_menu_item->AddChild("OTHER"); |
| |
| parent_menu->AddChild("Quit", |
| QUIT_CMD_EVENT); |
| parent_menu->AddChild("Tidy Target", |
| TIDY_CMD_EVENT); |
| |
| parent_menu->AddChild("View TARGET", |
| VIEW_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Show Image", |
| IMAGE_CMD_EVENT, FALSE); |
| parent_menu->AddChild("ShowBlock Outlines", |
| BLOCKS_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Show Baselines", |
| BASELINES_CMD_EVENT, FALSE); |
| parent_menu->AddChild("Write File", |
| WRITE_CMD_EVENT, imagebasename.string()); |
| parent_menu->AddChild("New Source File", |
| NEW_SOURCE_CMD_EVENT, imagebasename.string()); |
| parent_menu->AddChild("Uniform Display", |
| UNIFORM_DISP_CMD_EVENT); |
| parent_menu->AddChild("Refresh Display", |
| REFRESH_CMD_EVENT); |
| |
| return root_menu_item; |
| } |
| |
| } // namespace tesseract |
| |
| |
| /********************************************************************** |
| * display_bln_lines() |
| * |
| * Display normalised baseline, x-height, ascender limit and descender limit |
| **********************************************************************/ |
| |
| void display_bln_lines(ScrollView* window, |
| ScrollView::Color colour, |
| float scale_factor, |
| float y_offset, |
| float minx, |
| float maxx) { |
| window->Pen(colour); |
| window->Line(minx, y_offset + scale_factor * DESC_HEIGHT, |
| maxx, y_offset + scale_factor * DESC_HEIGHT); |
| window->Line(minx, y_offset + scale_factor * BL_HEIGHT, |
| maxx, y_offset + scale_factor * BL_HEIGHT); |
| window->Line(minx, y_offset + scale_factor * X_HEIGHT, |
| maxx, y_offset + scale_factor * X_HEIGHT); |
| window->Line(minx, y_offset + scale_factor * ASC_HEIGHT, |
| maxx, y_offset + scale_factor * ASC_HEIGHT); |
| } |
| |
| |
| /********************************************************************** |
| * do_new_source() |
| * |
| * Change to another source file. Automatically tidy page first |
| * |
| **********************************************************************/ |
| namespace tesseract { |
| void Tesseract::do_new_source( // serialise |
| ) { |
| FILE *infp; // input file |
| |
| char* name = image_win->ShowInputDialog("New Source File name"); |
| |
| STRING name_str(name); |
| delete[] name; |
| |
| if (source_changed) { |
| |
| int a = image_win->ShowYesNoDialog( |
| "Source changes will be LOST. Continue?(Y/N)"); |
| if (a != 'y') { image_win->AddMessage("Write cancelled"); return; } |
| |
| } |
| |
| // if not file exists |
| if (!(infp = fopen(name_str.string(), "r"))) { |
| |
| image_win->AddMessage("Cant open file " "%s" "", name_str.string()); |
| return; |
| } |
| |
| fclose(infp); |
| |
| image_win->AddMessage("Reading file " "%s" "...", name_str.string()); |
| source_block_list->clear(); |
| // appends to SOURCE |
| pgeditor_read_file(name_str, source_block_list); |
| source_changed = FALSE; |
| |
| image_win->AddMessage("Doing automatic Tidy Target..."); |
| viewing_source = FALSE; // Force viewing source |
| do_tidy_cmd(); |
| |
| image_win->AddMessage("Doing automatic Tidy Target...Done"); |
| |
| } |
| } // namespace tesseract |
| |
| |
| /********************************************************************** |
| * do_re_display() |
| * |
| * Redisplay page |
| **********************************************************************/ |
| |
| void |
| // function to call |
| do_re_display(BOOL8 word_painter( |
| BLOCK *, ROW *, WERD *)) { |
| BLOCK_IT block_it(current_block_list); |
| BLOCK *block; |
| int block_count = 1; |
| |
| ROW_IT row_it; |
| ROW *row; |
| |
| WERD_IT word_it; |
| WERD *word; |
| |
| image_win->Clear(); |
| if (display_image != 0) { |
| sv_show_sub_image(&page_image, 0, 0, |
| page_image.get_xsize(), page_image.get_ysize(), |
| image_win, 0, 0); |
| } |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block = block_it.data(); |
| row_it.set_to_list(block->row_list()); |
| for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { |
| row = row_it.data(); |
| word_it.set_to_list(row->word_list()); |
| for (word_it.mark_cycle_pt(); |
| !word_it.cycled_list(); word_it.forward()) { |
| word = word_it.data(); |
| word_painter(block, row, word); |
| } |
| if (display_baselines) |
| row->plot_baseline(image_win, ScrollView::GREEN); |
| } |
| if (display_blocks) |
| block->plot(image_win, block_count++, ScrollView::RED); |
| } |
| image_win->Update(); |
| } |
| |
| |
| /********************************************************************** |
| * do_tidy_cmd() |
| * |
| * Tidy TARGET page |
| **********************************************************************/ |
| |
| const TBOX do_tidy_cmd() { // tidy |
| ICOORD shift_vector; |
| TBOX tidy_box; // Just the tidy area |
| TBOX source_box; // source file area |
| |
| source_box = block_list_bounding_box(source_block_list); |
| // find src area |
| |
| if (!target_block_list.empty()) { |
| tidy_box = block_list_compress(&target_block_list); |
| |
| /* Shift tidied target above the source image area. */ |
| |
| shift_vector = ICOORD(0, source_box.top() + BLOCK_SPACING) |
| - tidy_box.botleft(); |
| block_list_move(&target_block_list, shift_vector); |
| tidy_box.move(shift_vector); |
| } |
| source_box += tidy_box; |
| // big enough for both |
| build_image_window(source_box); |
| do_view_cmd(); |
| return tidy_box; |
| } |
| |
| |
| /********************************************************************** |
| * do_view_cmd() |
| * |
| * View TARGET/View SOURCE command |
| **********************************************************************/ |
| |
| void do_view_cmd() { |
| viewing_source = !viewing_source; |
| image_win->Clear(); |
| if (viewing_source) { |
| current_block_list = source_block_list; |
| current_image_changed = &source_changed; |
| other_block_list = &target_block_list; |
| other_image_changed = &target_changed; |
| do_re_display(&word_display); |
| } |
| else { |
| current_block_list = &target_block_list; |
| current_image_changed = &target_changed; |
| other_block_list = source_block_list; |
| other_image_changed = &source_changed; |
| do_re_display(&word_display); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * do_write_file() |
| * |
| * Serialise a block list to file |
| * |
| * If writing image, tidy page and move to(0,0) first |
| **********************************************************************/ |
| |
| void do_write_file( // serialise |
| ) { |
| |
| char* name = image_win->ShowInputDialog("File Name"); |
| |
| FILE *infp; // input file |
| char msg_str[80]; |
| |
| TBOX enclosing_box; |
| |
| // if file exists |
| if ((infp = fopen(name, "r")) != NULL) { |
| fclose(infp); |
| sprintf(msg_str, "Overwrite file " "%s" "?(Y/N)", name); |
| |
| int a = image_win->ShowYesNoDialog(msg_str); |
| if (a != 'y') { image_win->AddMessage("Write cancelled"); delete[] name; return; } |
| } |
| |
| infp = fopen(name, "w"); // can we write to it? |
| if (infp == NULL) { |
| |
| image_win->AddMessage("Cant write to file " "%s" "", name); |
| delete[] name; |
| return; |
| } |
| fclose(infp); |
| |
| delete [] name; |
| |
| if (!viewing_source && !target_block_list.empty()) { |
| // Tidy & move to(0,0) |
| image_win->AddMessage("Automatic tidy..."); |
| viewing_source = TRUE; // Stay viewing target! |
| enclosing_box = do_tidy_cmd(); |
| block_list_move(&target_block_list, -enclosing_box.botleft()); |
| image_win->AddMessage("Writing file..."); |
| pgeditor_write_file(name, &target_block_list); |
| // move back |
| block_list_move(&target_block_list, |
| enclosing_box.botleft()); |
| } |
| else { |
| image_win->AddMessage("Writing file..."); |
| pgeditor_write_file(name, current_block_list); |
| } |
| image_win->AddMessage("Writing file...Done"); |
| *current_image_changed = FALSE; |
| |
| } |
| |
| /********************************************************************** |
| * notify() |
| * |
| * Event handler that processes incoming events, either forwarding |
| * them to process_cmd_win_event or process_image_event. |
| * |
| **********************************************************************/ |
| |
| void PGEventHandler::Notify(const SVEvent* event) { |
| char myval = '0'; |
| if (event->type == SVET_POPUP) { |
| ve->Notify(event); |
| } // These are handled by Var. Editor |
| else if (event->type == SVET_EXIT) { stillRunning = false; } |
| else if (event->type == SVET_MENU) { |
| if (strcmp(event->parameter, "true") == 0) { myval = 'T'; } |
| else if (strcmp(event->parameter, "false") == 0) { myval = 'F'; } |
| tess_->process_cmd_win_event(event->command_id, &myval); |
| } |
| else { |
| tess_->process_image_event(*event); |
| // else pgeditor_show_point(*event); |
| } |
| current_word_quit.set_value(FALSE); |
| selection_quit.set_value(FALSE); |
| // replot all var wins |
| } |
| |
| |
| /********************************************************************** |
| * pgeditor_main() |
| * |
| * Top level editor operation: |
| * Setup a new window and an according event handler |
| * |
| **********************************************************************/ |
| |
| namespace tesseract { |
| void Tesseract::pgeditor_main(BLOCK_LIST *blocks) { |
| |
| source_block_list = blocks; |
| current_block_list = blocks; |
| if (current_block_list->empty()) |
| return; |
| |
| stillRunning = true; |
| |
| build_image_window(block_list_bounding_box(source_block_list)); |
| word_display_mode.turn_on_bit(DF_EDGE_STEP); |
| do_re_display(&word_set_display); |
| #ifndef GRAPHICS_DISABLED |
| ve = new VariablesEditor(this, image_win); |
| #endif |
| PGEventHandler pgEventHandler(this); |
| |
| image_win->AddEventHandler(&pgEventHandler); |
| image_win->AddMessageBox(); |
| |
| SVMenuNode* svMenuRoot = build_menu_new(); |
| |
| svMenuRoot->BuildMenu(image_win); |
| image_win->SetVisible(true); |
| |
| image_win->AwaitEvent(SVET_DESTROY); |
| image_win->AddEventHandler(NULL); |
| } |
| } // namespace tesseract |
| |
| |
| /********************************************************************** |
| * pgeditor_msg() |
| * |
| * Display a message - in the command window if there is one, or to stdout |
| **********************************************************************/ |
| |
| void pgeditor_msg( // message display |
| const char *msg) { |
| image_win->AddMessage(msg); |
| } |
| |
| |
| /********************************************************************** |
| * pgeditor_read_file() |
| * |
| * Deserialise source file |
| **********************************************************************/ |
| |
| namespace tesseract { |
| void Tesseract::pgeditor_read_file( // of serialised file |
| STRING &filename, |
| BLOCK_LIST *blocks // block list to add to |
| ) { |
| STRING name = filename; //truncated name |
| const char *lastdot; //of name |
| TO_BLOCK_LIST land_blocks, port_blocks; |
| TBOX page_box; |
| |
| lastdot = strrchr (name.string (), '.'); |
| if (lastdot != NULL) |
| name[lastdot-name.string()] = '\0'; |
| if (!read_unlv_file(name, page_image.get_xsize(), page_image.get_ysize(), |
| blocks)) |
| FullPageBlock(page_image.get_xsize(), page_image.get_ysize(), blocks); |
| find_components(blocks, &land_blocks, &port_blocks, &page_box); |
| textord_page(page_box.topright(), blocks, &land_blocks, &port_blocks, this); |
| } |
| } // namespace tesseract |
| |
| /********************************************************************** |
| * pgeditor_show_point() |
| * |
| * Display the coordinates of a point in the command window |
| **********************************************************************/ |
| |
| void pgeditor_show_point( // display coords |
| SVEvent *event) { |
| image_win->AddMessage("Pointing at(%d, %d)", event->x, event->y); |
| } |
| |
| |
| /********************************************************************** |
| * pgeditor_write_file() |
| * |
| * Serialise a block list to file |
| * |
| **********************************************************************/ |
| |
| void pgeditor_write_file( // serialise |
| char *name, // file name |
| BLOCK_LIST *blocks // block list to write |
| ) { |
| FILE *infp; // input file |
| BLOCK_IT block_it(blocks); // block iterator |
| BLOCK *block; // current block |
| ROW_IT row_it; // row iterator |
| |
| infp = fopen(name, "w"); // create output file |
| if (infp == NULL) |
| CANTCREATEFILE.error("pgeditor_write_file", EXIT, name); |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block = block_it.extract(); |
| |
| row_it.set_to_list(block->row_list()); |
| for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) |
| // ensure correct |
| row_it.data()->recalc_bounding_box(); |
| |
| block->serialise(infp); // serialize non-empty |
| block_it.add_after_then_move(block); |
| } |
| fclose(infp); |
| } |
| |
| |
| /********************************************************************** |
| * process_cmd_win_event() |
| * |
| * Process a command returned from the command window |
| * (Just call the appropriate command handler) |
| **********************************************************************/ |
| |
| namespace tesseract { |
| BOOL8 Tesseract::process_cmd_win_event( // UI command semantics |
| inT32 cmd_event, // which menu item? |
| char *new_value // any prompt data |
| ) { |
| char msg[160]; |
| BOOL8 exit = FALSE; |
| |
| switch(cmd_event) { |
| case NULL_CMD_EVENT: |
| break; |
| |
| case VIEW_CMD_EVENT: |
| do_view_cmd(); |
| break; |
| case CHANGE_DISP_CMD_EVENT: |
| case DELETE_CMD_EVENT: |
| case CHANGE_TEXT_CMD_EVENT: |
| case TOGGLE_SEG_CMD_EVENT: |
| case DUMP_WERD_CMD_EVENT: |
| case SHOW_POINT_CMD_EVENT: |
| case ROW_SPACE_STAT_CMD_EVENT: |
| case BLOCK_SPACE_STAT_CMD_EVENT: |
| case SHOW_BLN_WERD_CMD_EVENT: |
| case SEGMENT_WERD_CMD_EVENT: |
| mode =(CMD_EVENTS) cmd_event; |
| break; |
| case COPY_CMD_EVENT: |
| mode =(CMD_EVENTS) cmd_event; |
| if (!viewing_source) |
| image_win->AddMessage("Can't COPY while viewing target!"); |
| break; |
| case BOUNDING_BOX_CMD_EVENT: |
| if (new_value[0] == 'T') |
| word_display_mode.turn_on_bit(DF_BOX); |
| else |
| word_display_mode.turn_off_bit(DF_BOX); |
| mode = CHANGE_DISP_CMD_EVENT; |
| break; |
| case CORRECT_TEXT_CMD_EVENT: |
| if (new_value[0] == 'T') |
| word_display_mode.turn_on_bit(DF_TEXT); |
| else |
| word_display_mode.turn_off_bit(DF_TEXT); |
| mode = CHANGE_DISP_CMD_EVENT; |
| break; |
| case POLYGONAL_CMD_EVENT: |
| if (new_value[0] == 'T') |
| word_display_mode.turn_on_bit(DF_POLYGONAL); |
| else |
| word_display_mode.turn_off_bit(DF_POLYGONAL); |
| mode = CHANGE_DISP_CMD_EVENT; |
| break; |
| case BL_NORM_CMD_EVENT: |
| if (new_value[0] == 'T') |
| word_display_mode.turn_on_bit(DF_BN_POLYGONAL); |
| else |
| word_display_mode.turn_off_bit(DF_BN_POLYGONAL); |
| mode = CHANGE_DISP_CMD_EVENT; |
| break; |
| case BITMAP_CMD_EVENT: |
| if (new_value[0] == 'T') |
| word_display_mode.turn_on_bit(DF_EDGE_STEP); |
| else |
| word_display_mode.turn_off_bit(DF_EDGE_STEP); |
| mode = CHANGE_DISP_CMD_EVENT; |
| break; |
| case UNIFORM_DISP_CMD_EVENT: |
| do_re_display(&word_set_display); |
| *current_image_changed = TRUE; |
| break; |
| case WRITE_CMD_EVENT: |
| do_write_file(); |
| break; |
| case TIDY_CMD_EVENT: |
| if (!target_block_list.empty()) { |
| viewing_source = TRUE; // Force viewing target |
| do_tidy_cmd(); |
| } |
| break; |
| case NEW_SOURCE_CMD_EVENT: |
| do_new_source(); |
| break; |
| case IMAGE_CMD_EVENT: |
| display_image =(new_value[0] == 'T'); |
| do_re_display(&word_display); |
| break; |
| case BLOCKS_CMD_EVENT: |
| display_blocks =(new_value[0] == 'T'); |
| do_re_display(&word_display); |
| break; |
| case BASELINES_CMD_EVENT: |
| display_baselines =(new_value[0] == 'T'); |
| do_re_display(&word_display); |
| break; |
| case REFRESH_CMD_EVENT: |
| do_re_display(&word_display); |
| break; |
| case QUIT_CMD_EVENT: |
| if (source_changed || target_changed) { |
| int a = image_win->ShowYesNoDialog( |
| "Changes not saved. Exit anyway?(Y/N)"); |
| if (a == 'y') { exit = TRUE; ScrollView::Exit(); } |
| } |
| else { |
| exit = TRUE; |
| ScrollView::Exit(); |
| } |
| break; |
| case RECOG_WERDS: |
| mode = RECOG_WERDS; |
| break; |
| case RECOG_PSEUDO: |
| mode = RECOG_PSEUDO; |
| break; |
| |
| default: |
| sprintf(msg, "Unrecognised event " INT32FORMAT "(%s)", |
| cmd_event, new_value); |
| image_win->AddMessage(msg); |
| break; |
| } |
| return exit; |
| } |
| |
| |
| /********************************************************************** |
| * process_image_event() |
| * |
| * User has done something in the image window - mouse down or up. Work out |
| * what it is and do something with it. |
| * If DOWN - just remember where it was. |
| * If UP - for each word in the selected area do the operation defined by |
| * the current mode. |
| **********************************************************************/ |
| void Tesseract::process_image_event( // action in image win |
| const SVEvent &event) { |
| static ICOORD down; |
| ICOORD up; |
| TBOX selection_box; |
| char msg[80]; |
| |
| switch(event.type) { |
| |
| case SVET_SELECTION: |
| if (event.type == SVET_SELECTION) { |
| down.set_x(event.x - event.x_size); |
| down.set_y(event.y + event.y_size); |
| if (mode == SHOW_POINT_CMD_EVENT) |
| show_point(current_block_list, event.x, event.y); |
| } |
| |
| up.set_x(event.x); |
| up.set_y(event.y); |
| |
| selection_box = TBOX(down, up); |
| |
| switch(mode) { |
| case CHANGE_DISP_CMD_EVENT: |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_blank_and_set_display); |
| break; |
| case COPY_CMD_EVENT: |
| if (!viewing_source) |
| image_win->AddMessage("Can't COPY while viewing target!"); |
| else |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_copy); |
| break; |
| case DELETE_CMD_EVENT: |
| ::process_selected_words_it(current_block_list, |
| selection_box, |
| &word_delete); |
| break; |
| case CHANGE_TEXT_CMD_EVENT: |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_change_text); |
| break; |
| case TOGGLE_SEG_CMD_EVENT: |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_toggle_seg); |
| break; |
| case DUMP_WERD_CMD_EVENT: |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_dumper); |
| break; |
| case SHOW_BLN_WERD_CMD_EVENT: |
| ::process_selected_words(current_block_list, |
| selection_box, |
| &word_bln_display); |
| break; |
| case SEGMENT_WERD_CMD_EVENT: |
| re_segment_word(current_block_list, selection_box); |
| break; |
| case ROW_SPACE_STAT_CMD_EVENT: |
| row_space_stat(current_block_list, selection_box); |
| break; |
| case BLOCK_SPACE_STAT_CMD_EVENT: |
| block_space_stat(current_block_list, selection_box); |
| break; |
| case SHOW_POINT_CMD_EVENT: |
| break; // ignore up event |
| |
| case RECOG_WERDS: |
| image_win->AddMessage("Recogging selected words"); |
| this->process_selected_words(current_block_list, |
| selection_box, |
| &Tesseract::recog_interactive); |
| break; |
| case RECOG_PSEUDO: |
| image_win->AddMessage("Recogging selected blobs"); |
| recog_pseudo_word(current_block_list, selection_box); |
| break; |
| |
| default: |
| sprintf(msg, "Mode %d not yet implemented", mode); |
| image_win->AddMessage(msg); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| } // namespace tesseract |
| |
| |
| /********************************************************************** |
| * re_scale_and_move_bln_word() |
| * |
| * Scale and move a bln word so that it fits in a specified bounding box. |
| * Scale by width or height to generate the largest image |
| **********************************************************************/ |
| |
| float re_scale_and_move_bln_word( // put bln word in box |
| WERD *norm_word, // BL normalised word |
| const TBOX &box // destination box |
| ) { |
| TBOX norm_box = norm_word->bounding_box(); |
| float width_scale_factor; |
| float height_scale_factor; |
| float selected_scale_factor; |
| |
| width_scale_factor = box.width() /(float) norm_box.width(); |
| height_scale_factor = box.height() /(float) ASC_HEIGHT; |
| |
| if ((ASC_HEIGHT * width_scale_factor) <= box.height()) |
| selected_scale_factor = width_scale_factor; |
| else |
| selected_scale_factor = height_scale_factor; |
| |
| norm_word->scale(selected_scale_factor); |
| norm_word->move(ICOORD((box.left() + box.width() / 2), box.bottom())); |
| return selected_scale_factor; |
| } |
| |
| |
| /********************************************************************** |
| * re_segment_word() |
| * |
| * If all selected blobs are in the same row, remove them from their current |
| * word(s) and put them in a new word. Insert the new word in the row at the |
| * appropriate point. Delete any empty words. |
| * |
| **********************************************************************/ |
| |
| void re_segment_word( // break/join words |
| BLOCK_LIST *block_list, // blocks to check |
| TBOX &selection_box) { |
| BLOCK_IT block_it(block_list); |
| BLOCK *block; |
| BLOCK *block_to_process = NULL; |
| ROW_IT row_it; |
| ROW *row; |
| ROW *row_to_process = NULL; |
| WERD_IT word_it; |
| WERD *word; |
| WERD *new_word = NULL; |
| BOOL8 polyg = false; |
| PBLOB_IT blob_it; |
| PBLOB_LIST dummy; // Just to initialize new_blob_it. |
| PBLOB_IT new_blob_it = &dummy; |
| PBLOB *blob; |
| |
| /* Find row to process - error if selections from more than one row */ |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block = block_it.data(); |
| if (block->bounding_box().overlap(selection_box)) { |
| row_it.set_to_list(block->row_list()); |
| for (row_it.mark_cycle_pt(); |
| !row_it.cycled_list(); row_it.forward()) { |
| row = row_it.data(); |
| if (row->bounding_box().overlap(selection_box)) { |
| if (row_to_process == NULL) { |
| block_to_process = block; |
| row_to_process = row; |
| } |
| else { |
| image_win->AddMessage("Cant resegment words " |
| "in more than one row"); |
| return; |
| } |
| } |
| } |
| } |
| } |
| /* Continue with row_to_process */ |
| |
| word_it.set_to_list(row_to_process->word_list()); |
| for (word_it.mark_cycle_pt(); !word_it.cycled_list(); word_it.forward()) { |
| word = word_it.data(); |
| polyg = word->flag(W_POLYGON); |
| if (word->bounding_box().overlap(selection_box)) { |
| blob_it.set_to_list(word->gblob_list()); |
| for (blob_it.mark_cycle_pt(); |
| !blob_it.cycled_list(); blob_it.forward()) { |
| blob = blob_it.data(); |
| if (gblob_bounding_box(blob, polyg).overlap(selection_box)) { |
| if (new_word == NULL) { |
| new_word = word->shallow_copy(); |
| new_blob_it.set_to_list(new_word->gblob_list()); |
| } |
| new_blob_it.add_to_end(blob_it.extract()); |
| // move blob |
| } |
| } |
| if (blob_it.empty()) { // no blobs in word |
| // so delete word |
| delete word_it.extract(); |
| } |
| } |
| } |
| if (new_word != NULL) { |
| gblob_sort_list(new_word->gblob_list(), polyg); |
| word_it.add_to_end(new_word); |
| word_it.sort(word_comparator); |
| row_to_process->bounding_box().plot(image_win, |
| ScrollView::BLACK, ScrollView::BLACK); |
| word_it.set_to_list(row_to_process->word_list()); |
| for (word_it.mark_cycle_pt(); |
| !word_it.cycled_list(); word_it.forward()) |
| word_display(block_to_process, row_to_process, word_it.data()); |
| *current_image_changed = TRUE; |
| } |
| } |
| |
| |
| void block_space_stat( // show space stats |
| BLOCK_LIST *block_list, // blocks to check |
| TBOX &selection_box) { |
| BLOCK_IT block_it(block_list); |
| BLOCK *block; |
| ROW_IT row_it; |
| ROW *row; |
| int block_idx = 0; |
| STATS all_gap_stats(0, MAXSPACING); |
| WERD_IT word_it; |
| WERD *word; |
| PBLOB_IT blob_it; |
| PBLOB *blob; |
| C_BLOB_IT cblob_it; |
| C_BLOB *cblob; |
| TBOX box; |
| inT16 prev_box_right; |
| inT16 gap_width; |
| inT16 min_inter_word_gap; |
| inT16 max_inter_char_gap; |
| |
| /* Find blocks to process */ |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block_idx++; |
| block = block_it.data(); |
| if (block->bounding_box().overlap(selection_box)) { |
| /* Process a block */ |
| tprintf("\nBlock %d\n", block_idx); |
| min_inter_word_gap = 3000; |
| max_inter_char_gap = 0; |
| all_gap_stats.clear(); |
| row_it.set_to_list(block->row_list()); |
| for (row_it.mark_cycle_pt(); |
| !row_it.cycled_list(); row_it.forward()) { |
| row = row_it.data(); |
| prev_box_right = -1; |
| word_it.set_to_list(row->word_list()); |
| for (word_it.mark_cycle_pt(); |
| !word_it.cycled_list(); word_it.forward()) { |
| word = word_it.data(); |
| if (word->flag(W_POLYGON)) { |
| blob_it.set_to_list(word->blob_list()); |
| for (blob_it.mark_cycle_pt(); |
| !blob_it.cycled_list(); blob_it.forward()) { |
| blob = blob_it.data(); |
| box = blob->bounding_box(); |
| if (prev_box_right > -1) { |
| gap_width = box.left() - prev_box_right; |
| all_gap_stats.add(gap_width, 1); |
| if (blob_it.at_first()) { |
| if (gap_width < min_inter_word_gap) |
| min_inter_word_gap = gap_width; |
| } |
| else { |
| if (gap_width > max_inter_char_gap) |
| max_inter_char_gap = gap_width; |
| } |
| } |
| prev_box_right = box.right(); |
| } |
| } |
| else { |
| cblob_it.set_to_list(word->cblob_list()); |
| for (cblob_it.mark_cycle_pt(); |
| !cblob_it.cycled_list(); cblob_it.forward()) { |
| cblob = cblob_it.data(); |
| box = cblob->bounding_box(); |
| if (prev_box_right > -1) { |
| gap_width = box.left() - prev_box_right; |
| all_gap_stats.add(gap_width, 1); |
| if (cblob_it.at_first()) { |
| if (gap_width < min_inter_word_gap) |
| min_inter_word_gap = gap_width; |
| } |
| else { |
| if (gap_width > max_inter_char_gap) |
| max_inter_char_gap = gap_width; |
| } |
| } |
| prev_box_right = box.right(); |
| } |
| } |
| } |
| } |
| tprintf("Max inter char gap = %d.\nMin inter word gap = %d.\n", |
| max_inter_char_gap, min_inter_word_gap); |
| all_gap_stats.short_print(NULL, TRUE); |
| all_gap_stats.smooth(2); |
| tprintf("SMOOTHED DATA...\n"); |
| all_gap_stats.short_print(NULL, TRUE); |
| } |
| } |
| } |
| |
| |
| void row_space_stat( // show space stats |
| BLOCK_LIST *block_list, // blocks to check |
| TBOX &selection_box) { |
| BLOCK_IT block_it(block_list); |
| BLOCK *block; |
| ROW_IT row_it; |
| ROW *row; |
| int block_idx = 0; |
| int row_idx; |
| STATS all_gap_stats(0, MAXSPACING); |
| WERD_IT word_it; |
| WERD *word; |
| PBLOB_IT blob_it; |
| PBLOB *blob; |
| C_BLOB_IT cblob_it; |
| C_BLOB *cblob; |
| TBOX box; |
| inT16 prev_box_right; |
| inT16 gap_width; |
| inT16 min_inter_word_gap; |
| inT16 max_inter_char_gap; |
| |
| /* Find rows to process */ |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block_idx++; |
| block = block_it.data(); |
| if (block->bounding_box().overlap(selection_box)) { |
| row_it.set_to_list(block->row_list()); |
| row_idx = 0; |
| for (row_it.mark_cycle_pt(); |
| !row_it.cycled_list(); row_it.forward()) { |
| row_idx++; |
| row = row_it.data(); |
| if (row->bounding_box().overlap(selection_box)) { |
| /* Process a row */ |
| |
| tprintf("\nBlock %d Row %d\n", block_idx, row_idx); |
| min_inter_word_gap = 3000; |
| max_inter_char_gap = 0; |
| prev_box_right = -1; |
| all_gap_stats.clear(); |
| word_it.set_to_list(row->word_list()); |
| for (word_it.mark_cycle_pt(); |
| !word_it.cycled_list(); word_it.forward()) { |
| word = word_it.data(); |
| if (word->flag(W_POLYGON)) { |
| blob_it.set_to_list(word->blob_list()); |
| for (blob_it.mark_cycle_pt(); |
| !blob_it.cycled_list(); blob_it.forward()) { |
| blob = blob_it.data(); |
| box = blob->bounding_box(); |
| if (prev_box_right > -1) { |
| gap_width = box.left() - prev_box_right; |
| all_gap_stats.add(gap_width, 1); |
| if (blob_it.at_first()) { |
| if (gap_width < min_inter_word_gap) |
| min_inter_word_gap = gap_width; |
| } |
| else { |
| if (gap_width > max_inter_char_gap) |
| max_inter_char_gap = gap_width; |
| } |
| } |
| prev_box_right = box.right(); |
| } |
| } |
| else { |
| cblob_it.set_to_list(word->cblob_list()); |
| for (cblob_it.mark_cycle_pt(); |
| !cblob_it.cycled_list(); cblob_it.forward()) { |
| cblob = cblob_it.data(); |
| box = cblob->bounding_box(); |
| if (prev_box_right > -1) { |
| gap_width = box.left() - prev_box_right; |
| all_gap_stats.add(gap_width, 1); |
| if (cblob_it.at_first()) { |
| if (gap_width < min_inter_word_gap) |
| min_inter_word_gap = gap_width; |
| } |
| else { |
| if (gap_width > max_inter_char_gap) |
| max_inter_char_gap = gap_width; |
| } |
| } |
| prev_box_right = box.right(); |
| } |
| } |
| } |
| tprintf |
| ("Max inter char gap = %d.\nMin inter word gap = %d.\n", |
| max_inter_char_gap, min_inter_word_gap); |
| all_gap_stats.short_print(NULL, TRUE); |
| all_gap_stats.smooth(2); |
| tprintf("SMOOTHED DATA...\n"); |
| all_gap_stats.short_print(NULL, TRUE); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /********************************************************************** |
| * show_point() |
| * |
| * Show coords of point, blob bounding box, word bounding box and offset from |
| * row baseline |
| **********************************************************************/ |
| |
| void show_point( // display posn of bloba word |
| BLOCK_LIST *block_list, // blocks to check |
| float x, |
| float y) { |
| FCOORD pt(x, y); |
| TBOX box; |
| BLOCK_IT block_it(block_list); |
| BLOCK *block; |
| ROW_IT row_it; |
| ROW *row; |
| WERD_IT word_it; |
| WERD *word; |
| PBLOB_IT blob_it; |
| PBLOB *blob; |
| C_BLOB_IT cblob_it; |
| C_BLOB *cblob; |
| |
| char msg[160]; |
| char *msg_ptr = msg; |
| |
| msg_ptr += sprintf(msg_ptr, "Pt:(%0.3f, %0.3f) ", x, y); |
| |
| for (block_it.mark_cycle_pt(); |
| !block_it.cycled_list(); block_it.forward()) { |
| block = block_it.data(); |
| if (block->bounding_box().contains(pt)) { |
| row_it.set_to_list(block->row_list()); |
| for (row_it.mark_cycle_pt(); |
| !row_it.cycled_list(); row_it.forward()) { |
| row = row_it.data(); |
| if (row->bounding_box().contains(pt)) { |
| msg_ptr += sprintf(msg_ptr, "BL(x)=%0.3f ", |
| row->base_line(x)); |
| |
| word_it.set_to_list(row->word_list()); |
| for (word_it.mark_cycle_pt(); |
| !word_it.cycled_list(); word_it.forward()) { |
| word = word_it.data(); |
| box = word->bounding_box(); |
| if (box.contains(pt)) { |
| msg_ptr += sprintf(msg_ptr, |
| "Wd(%d, %d)/(%d, %d) ", |
| box.left(), box.bottom(), |
| box.right(), box.top()); |
| |
| if (word->flag(W_POLYGON)) { |
| blob_it.set_to_list(word->blob_list()); |
| for (blob_it.mark_cycle_pt(); |
| !blob_it.cycled_list(); |
| blob_it.forward()) { |
| blob = blob_it.data(); |
| box = blob->bounding_box(); |
| if (box.contains(pt)) { |
| msg_ptr += sprintf(msg_ptr, |
| "Blb(%d, %d)/(%d, %d) ", |
| box.left(), |
| box.bottom(), |
| box.right(), |
| box.top()); |
| } |
| } |
| } |
| else { |
| cblob_it.set_to_list(word->cblob_list()); |
| for (cblob_it.mark_cycle_pt(); |
| !cblob_it.cycled_list(); |
| cblob_it.forward()) { |
| cblob = cblob_it.data(); |
| box = cblob->bounding_box(); |
| if (box.contains(pt)) { |
| msg_ptr += sprintf(msg_ptr, |
| "CBlb(%d, %d)/(%d, %d) ", |
| box.left(), |
| box.bottom(), |
| box.right(), |
| box.top()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| image_win->AddMessage(msg); |
| } |
| |
| |
| /********************************************************************** |
| * WERD PROCESSOR FUNCTIONS |
| * ======================== |
| * |
| * These routines are invoked by one or mode of: |
| * process_all_words() |
| * process_selected_words() |
| * or |
| * process_all_words_it() |
| * process_selected_words_it() |
| * for each word to be processed |
| **********************************************************************/ |
| |
| /********************************************************************** |
| * word_blank_and_set_display() Word processor |
| * |
| * Blank display of word then redisplay word according to current display mode |
| * settings |
| **********************************************************************/ |
| |
| BOOL8 word_blank_and_set_display( // display a word |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK); |
| return word_set_display(block, row, word); |
| } |
| |
| |
| /********************************************************************** |
| * word_bln_display() |
| * |
| * Normalise word and display in word window |
| **********************************************************************/ |
| |
| BOOL8 word_bln_display( // bln & display |
| BLOCK *, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| WERD *bln_word; |
| |
| bln_word = word->poly_copy(row->x_height()); |
| bln_word->baseline_normalise(row); |
| bln_word_window_handle()->Clear(); |
| display_bln_lines(bln_word_window_handle(), ScrollView::CYAN, |
| 1.0, 0.0f, -1000.0f, 1000.0f); |
| bln_word->plot(bln_word_window_handle(), ScrollView::RED); |
| delete bln_word; |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_change_text() |
| * |
| * Change the correct text of a word |
| **********************************************************************/ |
| |
| BOOL8 word_change_text( // change correct text |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| char* cp = image_win->ShowInputDialog( |
| "Enter/edit the correct text and press <<RETURN>>"); |
| word->set_text(cp); |
| delete[] cp; |
| |
| if (word_display_mode.bit(DF_TEXT) || word->display_flag(DF_TEXT)) { |
| word_blank_and_set_display(block, row, word); |
| ScrollView::Update(); |
| } |
| |
| *current_image_changed = TRUE; |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_copy() |
| * |
| * Copy a word to other display list |
| **********************************************************************/ |
| |
| BOOL8 word_copy( // copy a word |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| WERD *copy_word = new WERD; |
| |
| *copy_word = *word; |
| add_word(copy_word, row, block, other_block_list); |
| *other_image_changed = TRUE; |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_delete() |
| * |
| * Delete a word |
| **********************************************************************/ |
| |
| BOOL8 word_delete( // delete a word |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word, // word to be processed |
| BLOCK_IT &block_it, // block list iterator |
| ROW_IT &row_it, // row list iterator |
| WERD_IT &word_it // word list iterator |
| ) { |
| word_it.extract(); |
| word->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK); |
| delete(word); |
| |
| if (word_it.empty()) { // no words left in row |
| // so delete row |
| row_it.extract(); |
| row->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK); |
| delete(row); |
| |
| if (row_it.empty()) { // no rows left in blk |
| // so delete block |
| block_it.extract(); |
| block->bounding_box().plot(image_win, ScrollView::BLACK, ScrollView::BLACK); |
| delete(block); |
| } |
| } |
| *current_image_changed = TRUE; |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_display() Word Processor |
| * |
| * Display a word according to its display modes |
| **********************************************************************/ |
| |
| BOOL8 word_display( // display a word |
| BLOCK *, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| TBOX word_bb; // word bounding box |
| int word_height; // ht of word BB |
| BOOL8 displayed_something = FALSE; |
| BOOL8 displayed_rainbow = FALSE; |
| float shift; // from bot left |
| PBLOB_IT it; // blob iterator |
| C_BLOB_IT c_it; // cblob iterator |
| WERD *word_ptr; // poly copy |
| WERD temp_word; |
| float scale_factor; // for BN_POLYGON |
| |
| /* |
| Note the double coercions of(COLOUR)((inT32)editor_image_word_bb_color) |
| etc. are to keep the compiler happy. |
| */ |
| |
| // display bounding box |
| if (word->display_flag(DF_BOX)) { |
| word->bounding_box().plot(image_win, |
| (ScrollView::Color)((inT32) |
| editor_image_word_bb_color), |
| (ScrollView::Color)((inT32) |
| editor_image_word_bb_color)); |
| |
| ScrollView::Color c = (ScrollView::Color) |
| ((inT32) editor_image_blob_bb_color); |
| image_win->Pen(c); |
| if (word->flag(W_POLYGON)) { |
| it.set_to_list(word->blob_list()); |
| for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) |
| it.data()->bounding_box().plot(image_win); |
| } |
| else { |
| c_it.set_to_list(word->cblob_list()); |
| for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) |
| c_it.data()->bounding_box().plot(image_win); |
| } |
| displayed_something = TRUE; |
| } |
| |
| // display edge steps |
| if (word->display_flag(DF_EDGE_STEP) && |
| !word->flag(W_POLYGON)) { // edgesteps available |
| word->plot(image_win); // rainbow colors |
| displayed_something = TRUE; |
| displayed_rainbow = TRUE; |
| } |
| |
| // display poly approx |
| if (word->display_flag(DF_POLYGONAL)) { |
| // need to convert |
| if (!word->flag(W_POLYGON)) { |
| word_ptr = word->poly_copy(row->x_height()); |
| |
| /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ |
| |
| if (displayed_rainbow) |
| // ensure its visible |
| word_ptr->plot(image_win, ScrollView::WHITE); |
| else |
| // rainbow colors |
| word_ptr->plot(image_win); |
| delete word_ptr; |
| } |
| else { |
| if (displayed_rainbow) |
| // ensure its visible |
| word->plot(image_win, ScrollView::WHITE); |
| else |
| word->plot(image_win); // rainbow colors |
| } |
| |
| displayed_rainbow = TRUE; |
| displayed_something = TRUE; |
| } |
| |
| // disp BN poly approx |
| if (word->display_flag(DF_BN_POLYGONAL)) { |
| // need to convert |
| if (!word->flag(W_POLYGON)) { |
| word_ptr = word->poly_copy(row->x_height()); |
| temp_word = *word_ptr; |
| delete word_ptr; |
| |
| /* CALL POLYGONAL APPROXIMATOR WHEN AVAILABLE - on a temp_word */ |
| |
| } |
| else |
| temp_word = *word; // copy word |
| word_bb = word->bounding_box(); |
| if (!temp_word.flag(W_NORMALIZED)) |
| temp_word.baseline_normalise(row); |
| |
| scale_factor = re_scale_and_move_bln_word(&temp_word, word_bb); |
| display_bln_lines(image_win, ScrollView::CYAN, scale_factor, |
| word_bb.bottom(), word_bb.left(), word_bb.right()); |
| |
| if (displayed_rainbow) |
| // ensure its visible |
| temp_word.plot(image_win, ScrollView::WHITE); |
| else |
| temp_word.plot(image_win); // rainbow colors |
| |
| displayed_rainbow = TRUE; |
| displayed_something = TRUE; |
| } |
| |
| // display correct text |
| if (word->display_flag(DF_TEXT)) { |
| word_bb = word->bounding_box(); |
| ScrollView::Color c =(ScrollView::Color) |
| ((inT32) editor_image_blob_bb_color); |
| image_win->Pen(c); |
| word_height = word_bb.height(); |
| image_win->TextAttributes("Times", 0.75 * word_height, |
| false, false, false); |
| if (word_height < word_bb.width()) |
| shift = 0.25 * word_height; |
| else |
| shift = 0.0f; |
| |
| image_win->Text(word_bb.left() + shift, |
| word_bb.bottom() + 0.25 * word_height, word->text()); |
| |
| if (strlen(word->text()) > 0) |
| displayed_something = TRUE; |
| } |
| |
| if (!displayed_something) // display BBox anyway |
| word->bounding_box().plot(image_win, |
| (ScrollView::Color)((inT32) editor_image_word_bb_color), |
| (ScrollView::Color)((inT32) |
| editor_image_word_bb_color)); |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_dumper() |
| * |
| * Dump members to the debug window |
| **********************************************************************/ |
| |
| BOOL8 word_dumper( // dump word |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| |
| if (block != NULL) { |
| tprintf("\nBlock data...\n"); |
| block->print(NULL, FALSE); |
| } |
| tprintf("\nRow data...\n"); |
| row->print(NULL); |
| tprintf("\nWord data...\n"); |
| word->print(NULL); |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * word_set_display() Word processor |
| * |
| * Display word according to current display mode settings |
| **********************************************************************/ |
| |
| BOOL8 word_set_display( // display a word |
| BLOCK *block, // block holding word |
| ROW *row, // row holding word |
| WERD *word // word to be processed |
| ) { |
| TBOX word_bb; // word bounding box |
| |
| word->set_display_flag(DF_BOX, word_display_mode.bit(DF_BOX)); |
| word->set_display_flag(DF_TEXT, word_display_mode.bit(DF_TEXT)); |
| word->set_display_flag(DF_POLYGONAL, word_display_mode.bit(DF_POLYGONAL)); |
| word->set_display_flag(DF_EDGE_STEP, word_display_mode.bit(DF_EDGE_STEP)); |
| word->set_display_flag(DF_BN_POLYGONAL, |
| word_display_mode.bit(DF_BN_POLYGONAL)); |
| *current_image_changed = TRUE; |
| return word_display(block, row, word); |
| } |
| |
| |
| /********************************************************************** |
| * word_toggle_seg() |
| * |
| * Toggle the correct segmentation flag |
| **********************************************************************/ |
| |
| BOOL8 word_toggle_seg( // toggle seg flag |
| BLOCK *, // block holding word |
| ROW *, // row holding word |
| WERD *word // word to be processed |
| ) { |
| word->set_flag(W_SEGMENTED, !word->flag(W_SEGMENTED)); |
| *current_image_changed = TRUE; |
| return TRUE; |
| } |
| |
| #endif // GRAPHICS_DISABLED |
| |
| /* DEBUG ONLY */ |
| |
| void do_check_mem( // do it |
| inT32 level) { |
| check_mem("Doing it", level); |
| } |