/********************************************************************** * File: tstruct.cpp (Formerly tstruct.c) * Description: Code to manipulate the structures of the C++/C interface. * Author: Ray Smith * Created: Thu Apr 23 15:49:29 BST 1992 * * (C) Copyright 1992, 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 "mfcpch.h" #include "tfacep.h" #include "tstruct.h" //#include "structures.h" static ERRCODE BADFRAGMENTS = "Couldn't find matching fragment ends"; ELISTIZE (FRAGMENT) //extern /*"C"*/ oldoutline(TESSLINE*); /********************************************************************** * FRAGMENT::FRAGMENT * * Constructor for fragments. **********************************************************************/ FRAGMENT::FRAGMENT ( //constructor EDGEPT * head_pt, //start point EDGEPT * tail_pt //end point ):head (head_pt->pos.x, head_pt->pos.y), tail (tail_pt->pos.x, tail_pt->pos.y) { headpt = head_pt; // save ptrs tailpt = tail_pt; } // Helper function to make a fake PBLOB formed from the bounding box // of the given old-format outline. static PBLOB* MakeRectBlob(TESSLINE* ol) { POLYPT_LIST poly_list; POLYPT_IT poly_it = &poly_list; FCOORD pos, vec; POLYPT *polypt; // Create points at each of the 4 corners of the rectangle in turn. pos = FCOORD(ol->topleft.x, ol->topleft.y); vec = FCOORD(0.0f, ol->botright.y - ol->topleft.y); polypt = new POLYPT(pos, vec); poly_it.add_after_then_move(polypt); pos = FCOORD(ol->topleft.x, ol->botright.y); vec = FCOORD(ol->botright.x - ol->topleft.x, 0.0f); polypt = new POLYPT(pos, vec); poly_it.add_after_then_move(polypt); pos = FCOORD(ol->botright.x, ol->botright.y); vec = FCOORD(0.0f, ol->topleft.y - ol->botright.y); polypt = new POLYPT(pos, vec); poly_it.add_after_then_move(polypt); pos = FCOORD(ol->botright.x, ol->topleft.y); vec = FCOORD(ol->topleft.x - ol->botright.x, 0.0f); polypt = new POLYPT(pos, vec); poly_it.add_after_then_move(polypt); OUTLINE_LIST out_list; OUTLINE_IT out_it = &out_list; out_it.add_after_then_move(new OUTLINE(&poly_it)); return new PBLOB(&out_list); } /********************************************************************** * make_ed_word * * Make an editor format word from the tess style word. **********************************************************************/ WERD *make_ed_word( //construct word TWERD *tessword, //word to convert WERD *clone //clone this one ) { WERD *word; //converted word TBLOB *tblob; //current blob PBLOB *blob; //new blob PBLOB_LIST blobs; //list of blobs PBLOB_IT blob_it = &blobs; //iterator for (tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) { blob = make_ed_blob (tblob); if (blob == NULL && tblob->outlines != NULL) { // Make a fake blob using the bounding box rectangle of the 1st outline. blob = MakeRectBlob(tblob->outlines); } if (blob != NULL) { blob_it.add_after_then_move (blob); } } if (!blobs.empty ()) word = new WERD (&blobs, clone); else word = NULL; return word; } /********************************************************************** * make_ed_blob * * Make an editor format blob from the tess style blob. **********************************************************************/ PBLOB *make_ed_blob( //construct blob TBLOB *tessblob //blob to convert ) { TESSLINE *tessol; //tess outline FRAGMENT_LIST fragments; //list of fragments OUTLINE *outline; //current outline OUTLINE_LIST out_list; //list of outlines OUTLINE_IT out_it = &out_list; //iterator for (tessol = tessblob->outlines; tessol != NULL; tessol = tessol->next) { //stick in list register_outline(tessol, &fragments); } while (!fragments.empty ()) { outline = make_ed_outline (&fragments); if (outline != NULL) { out_it.add_after_then_move (outline); } } if (out_it.empty()) return NULL; //couldn't do it return new PBLOB (&out_list); //turn to blob } /********************************************************************** * make_ed_outline * * Make an editor format outline from the list of fragments. **********************************************************************/ OUTLINE *make_ed_outline( //constructoutline FRAGMENT_LIST *list //list of fragments ) { FRAGMENT *fragment; //current fragment EDGEPT *edgept; //current point ICOORD headpos; //coords of head ICOORD tailpos; //coords of tail FCOORD pos; //coords of edgept FCOORD vec; //empty POLYPT *polypt; //current point POLYPT_LIST poly_list; //list of point POLYPT_IT poly_it = &poly_list;//iterator FRAGMENT_IT fragment_it = list;//fragment headpos = fragment_it.data ()->head; do { fragment = fragment_it.data (); edgept = fragment->headpt; //start of segment do { pos = FCOORD (edgept->pos.x, edgept->pos.y); vec = FCOORD (edgept->vec.x, edgept->vec.y); polypt = new POLYPT (pos, vec); //add to list poly_it.add_after_then_move (polypt); edgept = edgept->next; } while (edgept != fragment->tailpt); tailpos = ICOORD (edgept->pos.x, edgept->pos.y); //get rid of it delete fragment_it.extract (); if (tailpos != headpos) { if (fragment_it.empty ()) { return NULL; } fragment_it.forward (); //find next segment for (fragment_it.mark_cycle_pt (); !fragment_it.cycled_list () && fragment_it.data ()->head != tailpos; fragment_it.forward ()); if (fragment_it.data ()->head != tailpos) { // It is legitimate for the heads to not all match to tails, // since not all combinations of seams always make sense. for (fragment_it.mark_cycle_pt (); !fragment_it.cycled_list (); fragment_it.forward ()) { fragment = fragment_it.extract (); delete fragment; } return NULL; //can't do it } } } while (tailpos != headpos); return new OUTLINE (&poly_it); //turn to outline } /********************************************************************** * register_outline * * Add the fragments in the given outline to the list **********************************************************************/ void register_outline( //add fragments TESSLINE *outline, //tess format FRAGMENT_LIST *list //list to add to ) { EDGEPT *startpt; //start of outline EDGEPT *headpt; //start of fragment EDGEPT *tailpt; //end of fragment FRAGMENT *fragment; //new fragment FRAGMENT_IT it = list; //iterator startpt = outline->loop; do { startpt = startpt->next; if (startpt == NULL) return; //illegal! } while (startpt->flags[0] == 0 && startpt != outline->loop); headpt = startpt; do startpt = startpt->next; while (startpt->flags[0] != 0 && startpt != headpt); if (startpt->flags[0] != 0) return; //all hidden! headpt = startpt; do { tailpt = headpt; do tailpt = tailpt->next; while (tailpt->flags[0] == 0 && tailpt != startpt); fragment = new FRAGMENT (headpt, tailpt); it.add_after_then_move (fragment); while (tailpt->flags[0] != 0) tailpt = tailpt->next; headpt = tailpt; } while (tailpt != startpt); } /********************************************************************** * convert_choice_lists * * Convert the ARRAY of TESS_LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. **********************************************************************/ void convert_choice_lists( //convert lists ARRAY tessarray, //list from tess BLOB_CHOICE_LIST_CLIST *ratings //list of results ) { inT32 length; //elements in array inT32 index; //index to array LIST result; //tess output //iterator BLOB_CHOICE_LIST_C_IT it = ratings; BLOB_CHOICE_LIST *choice; //created choice if (tessarray != NULL) { length = array_count (tessarray); for (index = 0; index < length; index++) { result = (LIST) array_value (tessarray, index); //make one choice = new BLOB_CHOICE_LIST; //convert blob choices convert_choice_list(result, *choice); //add to super list it.add_after_then_move (choice); } free_mem(tessarray); //lists already freed } } /********************************************************************** * convert_choice_list * * Convert the LIST of TESS_CHOICEs into a BLOB_CHOICE_LIST. **********************************************************************/ void convert_choice_list( //convert lists LIST list, //list from tess BLOB_CHOICE_LIST &ratings //list of results ) { LIST result; //tess output BLOB_CHOICE_IT it = &ratings; //iterator BLOB_CHOICE *choice; //created choice A_CHOICE *tesschoice; //choice to convert for (result = list; result != NULL; result = result->next) { //traverse list tesschoice = (A_CHOICE *) result->node; //make one choice = new BLOB_CHOICE (tesschoice->string, tesschoice->rating, tesschoice->certainty, tesschoice->config, tesschoice->script); it.add_after_then_move (choice); } destroy_nodes (list, (void (*)(void *)) free_choice); //get rid of it } /********************************************************************** * make_tess_row * * Make a fake row structure to pass to the tesseract matchers. **********************************************************************/ void make_tess_row( //make fake row DENORM *denorm, //row info TEXTROW *tessrow //output row ) { tessrow->baseline.segments = 1; tessrow->baseline.xstarts[0] = -32767; tessrow->baseline.xstarts[1] = 32767; tessrow->baseline.quads[0].a = 0; tessrow->baseline.quads[0].b = 0; tessrow->baseline.quads[0].c = bln_baseline_offset; tessrow->xheight.segments = 1; tessrow->xheight.xstarts[0] = -32767; tessrow->xheight.xstarts[1] = 32767; tessrow->xheight.quads[0].a = 0; tessrow->xheight.quads[0].b = 0; tessrow->xheight.quads[0].c = bln_x_height + bln_baseline_offset; tessrow->lineheight = bln_x_height; if (denorm != NULL) { tessrow->ascrise = denorm->row ()->ascenders () * denorm->scale (); tessrow->descdrop = denorm->row ()->descenders () * denorm->scale (); } else { tessrow->ascrise = bln_baseline_offset; tessrow->descdrop = -bln_baseline_offset; } } /********************************************************************** * make_tess_word * * Convert the word to Tess format. **********************************************************************/ TWERD *make_tess_word( //convert owrd WERD *word, //word to do TEXTROW *row //fake row ) { TWERD *tessword; //tess format tessword = newword (); //use old allocator tessword->row = row; //give them something //copy string tessword->correct = strsave (word->text ()); tessword->guess = NULL; tessword->blobs = make_tess_blobs (word->blob_list ()); tessword->blanks = 1; tessword->blobcount = word->blob_list ()->length (); tessword->next = NULL; return tessword; } /********************************************************************** * make_tess_blobs * * Make Tess style blobs from a list of BLOBs. **********************************************************************/ TBLOB *make_tess_blobs( //make tess blobs PBLOB_LIST *bloblist //list to convert ) { PBLOB_IT it = bloblist; //iterator PBLOB *blob; //current blob TBLOB *head; //output list TBLOB *tail; //end of list TBLOB *tessblob; head = NULL; tail = NULL; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { blob = it.data (); tessblob = make_tess_blob (blob, TRUE); if (head) tail->next = tessblob; else head = tessblob; tail = tessblob; } return head; } /********************************************************************** * make_tess_blob * * Make a single Tess style blob **********************************************************************/ TBLOB *make_tess_blob( //make tess blob PBLOB *blob, //blob to convert BOOL8 flatten //flatten outline structure ) { inT32 index; TBLOB *tessblob; tessblob = newblob (); tessblob->outlines = (struct olinestruct *) make_tess_outlines (blob->out_list (), flatten); for (index = 0; index < TBLOBFLAGS; index++) tessblob->flags[index] = 0; //!! tessblob->correct = 0; tessblob->guess = 0; for (index = 0; index < MAX_WO_CLASSES; index++) { tessblob->classes[index] = 0; tessblob->values[index] = 0; } tessblob->next = NULL; return tessblob; } /********************************************************************** * make_tess_outlines * * Make Tess style outlines from a list of OUTLINEs. **********************************************************************/ TESSLINE *make_tess_outlines( //make tess outlines OUTLINE_LIST *outlinelist, //list to convert BOOL8 flatten //flatten outline structure ) { OUTLINE_IT it = outlinelist; //iterator OUTLINE *outline; //current outline TESSLINE *head; //output list TESSLINE *tail; //end of list TESSLINE *tessoutline; head = NULL; tail = NULL; for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) { outline = it.data (); tessoutline = newoutline (); tessoutline->compactloop = NULL; tessoutline->loop = make_tess_edgepts (outline->polypts (), tessoutline->topleft, tessoutline->botright); if (tessoutline->loop == NULL) { oldoutline(tessoutline); continue; } tessoutline->start = tessoutline->loop->pos; tessoutline->node = NULL; tessoutline->next = NULL; tessoutline->child = NULL; if (!outline->child ()->empty ()) { if (flatten) tessoutline->next = (struct olinestruct *) make_tess_outlines (outline->child (), flatten); else { tessoutline->next = NULL; tessoutline->child = (struct olinestruct *) make_tess_outlines (outline->child (), flatten); } } else tessoutline->next = NULL; if (head) tail->next = tessoutline; else head = tessoutline; while (tessoutline->next != NULL) tessoutline = tessoutline->next; tail = tessoutline; } return head; } /********************************************************************** * make_tess_edgepts * * Make Tess style edgepts from a list of POLYPTs. **********************************************************************/ EDGEPT *make_tess_edgepts( //make tess edgepts POLYPT_LIST *edgeptlist, //list to convert TPOINT &tl, //bounding box TPOINT &br) { inT32 index; POLYPT_IT it = edgeptlist; //iterator POLYPT *edgept; //current edgept EDGEPT *head; //output list EDGEPT *tail; //end of list EDGEPT *tessedgept; head = NULL; tail = NULL; tl.x = MAX_INT16; tl.y = -MAX_INT16; br.x = -MAX_INT16; br.y = MAX_INT16; for (it.mark_cycle_pt (); !it.cycled_list ();) { edgept = it.data (); tessedgept = newedgept (); tessedgept->pos.x = (inT16) edgept->pos.x (); tessedgept->pos.y = (inT16) edgept->pos.y (); if (tessedgept->pos.x < tl.x) tl.x = tessedgept->pos.x; if (tessedgept->pos.x > br.x) br.x = tessedgept->pos.x; if (tessedgept->pos.y > tl.y) tl.y = tessedgept->pos.y; if (tessedgept->pos.y < br.y) br.y = tessedgept->pos.y; if (head != NULL && tessedgept->pos.x == tail->pos.x && tessedgept->pos.y == tail->pos.y) { oldedgept(tessedgept); } else { for (index = 0; index < EDGEPTFLAGS; index++) tessedgept->flags[index] = 0; if (head != NULL) { tail->vec.x = tessedgept->pos.x - tail->pos.x; tail->vec.y = tessedgept->pos.y - tail->pos.y; tessedgept->prev = tail; } tessedgept->next = head; if (head) tail->next = tessedgept; else head = tessedgept; tail = tessedgept; } it.forward (); } head->prev = tail; tail->vec.x = head->pos.x - tail->pos.x; tail->vec.y = head->pos.y - tail->pos.y; if (head == tail) { oldedgept(head); return NULL; //empty } return head; }