tesseract  5.0.0-alpha-619-ge9db
fpchop.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: fpchop.cpp (Formerly fp_chop.c)
3  * Description: Code to chop fixed pitch text into character cells.
4  * Author: Ray Smith
5  *
6  * (C) Copyright 1993, Hewlett-Packard Ltd.
7  ** Licensed under the Apache License, Version 2.0 (the "License");
8  ** you may not use this file except in compliance with the License.
9  ** You may obtain a copy of the License at
10  ** http://www.apache.org/licenses/LICENSE-2.0
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  *
17  **********************************************************************/
18 
19 #include "blobbox.h"
20 #include "statistc.h"
21 #include "drawtord.h"
22 #include "tovars.h"
23 #include "topitch.h"
24 #include "fpchop.h"
25 
26 // Include automatically generated configuration file if running autoconf.
27 #ifdef HAVE_CONFIG_H
28 #include "config_auto.h"
29 #endif
30 
32 "Max allowed bending of chop cells");
34 "Max distance of chop pt from vertex");
35 
37 //#undef ASSERT_HOST
38 //#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x);
39 /**********************************************************************
40  * fixed_pitch_words
41  *
42  * Make a ROW from a fixed pitch TO_ROW.
43  **********************************************************************/
44 ROW *fixed_pitch_words( //find lines
45  TO_ROW *row, //row to do
46  FCOORD rotation //for drawing
47  ) {
48  bool bol; //start of line
49  uint8_t blanks; //in front of word
50  uint8_t new_blanks; //blanks in empty cell
51  int16_t chop_coord; //chop boundary
52  int16_t prev_chop_coord; //start of cell
53  int16_t rep_left; //left edge of rep word
54  ROW *real_row; //output row
55  C_OUTLINE_LIST left_coutlines;
56  C_OUTLINE_LIST right_coutlines;
57  C_BLOB_LIST cblobs;
58  C_BLOB_IT cblob_it = &cblobs;
59  WERD_LIST words;
60  WERD_IT word_it = &words; //new words
61  //repeated blobs
62  WERD_IT rep_it = &row->rep_words;
63  WERD *word; //new word
64  int32_t xstarts[2]; //row ends
65  int32_t prev_x; //end of prev blob
66  //iterator
67  BLOBNBOX_IT box_it = row->blob_list ();
68  //boundaries
69  ICOORDELT_IT cell_it = &row->char_cells;
70 
71 #ifndef GRAPHICS_DISABLED
72  if (textord_show_page_cuts && to_win != nullptr) {
73  plot_row_cells (to_win, ScrollView::RED, row, 0, &row->char_cells);
74  }
75 #endif
76 
77  prev_x = -INT16_MAX;
78  bol = true;
79  blanks = 0;
80  if (rep_it.empty ())
81  rep_left = INT16_MAX;
82  else
83  rep_left = rep_it.data ()->bounding_box ().left ();
84  if (box_it.empty ())
85  return nullptr; //empty row
86  xstarts[0] = box_it.data ()->bounding_box ().left ();
87  if (rep_left < xstarts[0]) {
88  xstarts[0] = rep_left;
89  }
90  if (cell_it.empty () || row->char_cells.singleton ()) {
91  tprintf ("Row without enough char cells!\n");
92  tprintf ("Leftmost blob is at (%d,%d)\n",
93  box_it.data ()->bounding_box ().left (),
94  box_it.data ()->bounding_box ().bottom ());
95  return nullptr;
96  }
97  ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ());
98  prev_chop_coord = cell_it.data ()->x ();
99  word = nullptr;
100  while (rep_left < cell_it.data ()->x ()) {
101  word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
102  blanks, row->fixed_pitch, &word_it);
103  }
104  cell_it.mark_cycle_pt ();
105  if (prev_chop_coord >= cell_it.data ()->x ())
106  cell_it.forward ();
107  for (; !cell_it.cycled_list (); cell_it.forward ()) {
108  chop_coord = cell_it.data ()->x ();
109  while (!box_it.empty ()
110  && box_it.data ()->bounding_box ().left () <= chop_coord) {
111  if (box_it.data ()->bounding_box ().right () > prev_x)
112  prev_x = box_it.data ()->bounding_box ().right ();
113  split_to_blob (box_it.extract (), chop_coord,
114  textord_fp_chop_error + 0.5f,
115  &left_coutlines,
116  &right_coutlines);
117  box_it.forward ();
118  while (!box_it.empty() && box_it.data()->cblob() == nullptr) {
119  delete box_it.extract();
120  box_it.forward();
121  }
122  }
123  if (!right_coutlines.empty() && left_coutlines.empty())
124  split_to_blob (nullptr, chop_coord,
125  textord_fp_chop_error + 0.5f,
126  &left_coutlines,
127  &right_coutlines);
128  if (!left_coutlines.empty()) {
129  cblob_it.add_after_then_move(new C_BLOB(&left_coutlines));
130  } else {
131  if (rep_left < chop_coord) {
132  if (rep_left > prev_chop_coord)
133  new_blanks = static_cast<uint8_t>(floor ((rep_left - prev_chop_coord)
134  / row->fixed_pitch + 0.5));
135  else
136  new_blanks = 0;
137  }
138  else {
139  if (chop_coord > prev_chop_coord)
140  new_blanks = static_cast<uint8_t>(floor ((chop_coord - prev_chop_coord)
141  / row->fixed_pitch + 0.5));
142  else
143  new_blanks = 0;
144  }
145  if (!cblob_it.empty()) {
146  if (blanks < 1 && word != nullptr && !word->flag (W_REP_CHAR))
147  blanks = 1;
148  word = new WERD (&cblobs, blanks, nullptr);
149  cblob_it.set_to_list (&cblobs);
150  word->set_flag (W_DONT_CHOP, true);
151  word_it.add_after_then_move (word);
152  if (bol) {
153  word->set_flag (W_BOL, true);
154  bol = false;
155  }
156  blanks = new_blanks;
157  }
158  else
159  blanks += new_blanks;
160  while (rep_left < chop_coord) {
161  word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
162  blanks, row->fixed_pitch, &word_it);
163  }
164  }
165  if (prev_chop_coord < chop_coord)
166  prev_chop_coord = chop_coord;
167  }
168  if (!cblob_it.empty()) {
169  word = new WERD(&cblobs, blanks, nullptr);
170  word->set_flag (W_DONT_CHOP, true);
171  word_it.add_after_then_move (word);
172  if (bol)
173  word->set_flag (W_BOL, true);
174  }
175  ASSERT_HOST (word != nullptr);
176  while (!rep_it.empty ()) {
177  add_repeated_word (&rep_it, rep_left, prev_chop_coord,
178  blanks, row->fixed_pitch, &word_it);
179  }
180  //at end of line
181  word_it.data ()->set_flag (W_EOL, true);
182  if (prev_chop_coord > prev_x)
183  prev_x = prev_chop_coord;
184  xstarts[1] = prev_x + 1;
185  real_row = new ROW (row, static_cast<int16_t>(row->kern_size), static_cast<int16_t>(row->space_size));
186  word_it.set_to_list (real_row->word_list ());
187  //put words in row
188  word_it.add_list_after (&words);
189  real_row->recalc_bounding_box ();
190  return real_row;
191 }
192 
193 
194 /**********************************************************************
195  * add_repeated_word
196  *
197  * Add repeated word into the row at the given point.
198  **********************************************************************/
199 
200 WERD *add_repeated_word( //move repeated word
201  WERD_IT *rep_it, //repeated words
202  int16_t &rep_left, //left edge of word
203  int16_t &prev_chop_coord, //previous word end
204  uint8_t &blanks, //no of blanks
205  float pitch, //char cell size
206  WERD_IT *word_it //list of words
207  ) {
208  WERD *word; //word to move
209  int16_t new_blanks; //extra blanks
210 
211  if (rep_left > prev_chop_coord) {
212  new_blanks = static_cast<uint8_t>(floor ((rep_left - prev_chop_coord) / pitch + 0.5));
213  blanks += new_blanks;
214  }
215  word = rep_it->extract ();
216  prev_chop_coord = word->bounding_box ().right ();
217  word_it->add_after_then_move (word);
218  word->set_blanks (blanks);
219  rep_it->forward ();
220  if (rep_it->empty ())
221  rep_left = INT16_MAX;
222  else
223  rep_left = rep_it->data ()->bounding_box ().left ();
224  blanks = 0;
225  return word;
226 }
227 
228 
229 /**********************************************************************
230  * split_to_blob
231  *
232  * Split a BLOBNBOX across a vertical chop line and put the pieces
233  * into a left outline list and a right outline list.
234  **********************************************************************/
235 
236 void split_to_blob( //split the blob
237  BLOBNBOX *blob, //blob to split
238  int16_t chop_coord, //place to chop
239  float pitch_error, //allowed deviation
240  C_OUTLINE_LIST *left_coutlines, //for cblobs
241  C_OUTLINE_LIST *right_coutlines) {
242  C_BLOB *real_cblob; //cblob to chop
243 
244  if (blob != nullptr) {
245  real_cblob = blob->cblob();
246  } else {
247  real_cblob = nullptr;
248  }
249  if (!right_coutlines->empty() || real_cblob != nullptr)
250  fixed_chop_cblob(real_cblob,
251  chop_coord,
252  pitch_error,
253  left_coutlines,
254  right_coutlines);
255 
256  delete blob;
257 }
258 
259 /**********************************************************************
260  * fixed_chop_cblob
261  *
262  * Chop the given cblob (if any) and the existing right outlines to
263  * produce a list of outlines left of the chop point and more to the right.
264  **********************************************************************/
265 
266 void fixed_chop_cblob( //split the blob
267  C_BLOB *blob, //blob to split
268  int16_t chop_coord, //place to chop
269  float pitch_error, //allowed deviation
270  C_OUTLINE_LIST *left_outlines, //left half of chop
271  C_OUTLINE_LIST *right_outlines //right half of chop
272  ) {
273  C_OUTLINE *old_right; //already there
274  C_OUTLINE_LIST new_outlines; //new right ones
275  //output iterator
276  C_OUTLINE_IT left_it = left_outlines;
277  //in/out iterator
278  C_OUTLINE_IT right_it = right_outlines;
279  C_OUTLINE_IT new_it = &new_outlines;
280  C_OUTLINE_IT blob_it; //outlines in blob
281 
282  if (!right_it.empty ()) {
283  while (!right_it.empty ()) {
284  old_right = right_it.extract ();
285  right_it.forward ();
286  fixed_split_coutline(old_right,
287  chop_coord,
288  pitch_error,
289  &left_it,
290  &new_it);
291  }
292  right_it.add_list_before (&new_outlines);
293  }
294  if (blob != nullptr) {
295  blob_it.set_to_list (blob->out_list ());
296  for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
297  blob_it.forward ())
298  fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error,
299  &left_it, &right_it);
300  delete blob;
301  }
302 }
303 
304 
305 /**********************************************************************
306  * fixed_split_outline
307  *
308  * Chop the given outline (if necessary) placing the fragments which
309  * fall either side of the chop line into the appropriate list.
310  **********************************************************************/
311 
312 void fixed_split_coutline( //chop the outline
313  C_OUTLINE *srcline, //source outline
314  int16_t chop_coord, //place to chop
315  float pitch_error, //allowed deviation
316  C_OUTLINE_IT *left_it, //left half of chop
317  C_OUTLINE_IT *right_it //right half of chop
318  ) {
319  C_OUTLINE *child; //child outline
320  TBOX srcbox; //box of outline
321  C_OUTLINE_LIST left_ch; //left children
322  C_OUTLINE_LIST right_ch; //right children
323  C_OUTLINE_FRAG_LIST left_frags;//chopped fragments
324  C_OUTLINE_FRAG_LIST right_frags;;
325  C_OUTLINE_IT left_ch_it = &left_ch;
326  //for whole children
327  C_OUTLINE_IT right_ch_it = &right_ch;
328  //for holes
329  C_OUTLINE_IT child_it = srcline->child ();
330 
331  srcbox = srcline->bounding_box();
332  if (srcbox.left() + srcbox.right() <= chop_coord * 2
333  && srcbox.right() < chop_coord + pitch_error) {
334  // Whole outline is in the left side or not far over the chop_coord,
335  // so put the whole thing on the left.
336  left_it->add_after_then_move(srcline);
337  } else if (srcbox.left() + srcbox.right() > chop_coord * 2
338  && srcbox.left () > chop_coord - pitch_error) {
339  // Whole outline is in the right side or not far over the chop_coord,
340  // so put the whole thing on the right.
341  right_it->add_before_stay_put(srcline);
342  } else {
343  // Needs real chopping.
344  if (fixed_chop_coutline(srcline, chop_coord, pitch_error,
345  &left_frags, &right_frags)) {
346  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
347  child_it.forward()) {
348  child = child_it.extract();
349  srcbox = child->bounding_box();
350  if (srcbox.right() < chop_coord) {
351  // Whole child is on the left.
352  left_ch_it.add_after_then_move(child);
353  } else if (srcbox.left() > chop_coord) {
354  // Whole child is on the right.
355  right_ch_it.add_after_then_move (child);
356  } else {
357  // No pitch_error is allowed when chopping children to prevent
358  // impossible outlines from being created.
359  if (fixed_chop_coutline(child, chop_coord, 0.0f,
360  &left_frags, &right_frags)) {
361  delete child;
362  } else {
363  if (srcbox.left() + srcbox.right() <= chop_coord * 2)
364  left_ch_it.add_after_then_move(child);
365  else
366  right_ch_it.add_after_then_move(child);
367  }
368  }
369  }
370  close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it);
371  close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it);
372  ASSERT_HOST(left_ch.empty() && right_ch.empty());
373  // No children left.
374  delete srcline; // Smashed up.
375  } else {
376  // Chop failed. Just use middle coord.
377  if (srcbox.left() + srcbox.right() <= chop_coord * 2)
378  left_it->add_after_then_move(srcline); // Stick whole in left.
379  else
380  right_it->add_before_stay_put(srcline);
381  }
382  }
383 }
384 
385 
386 /**********************************************************************
387  * fixed_chop_coutline
388  *
389  * Chop the given coutline (if necessary) placing the fragments which
390  * fall either side of the chop line into the appropriate list.
391  * If the coutline lies too heavily to one side to chop, false is returned.
392  **********************************************************************/
393 
394 bool fixed_chop_coutline( //chop the outline
395  C_OUTLINE* srcline, //source outline
396  int16_t chop_coord, //place to chop
397  float pitch_error, //allowed deviation
398  C_OUTLINE_FRAG_LIST* left_frags, //left half of chop
399  C_OUTLINE_FRAG_LIST* right_frags //right half of chop
400 ) {
401  bool first_frag; //fragment
402  int16_t left_edge; //of outline
403  int16_t startindex; //in first fragment
404  int32_t length; //of outline
405  int16_t stepindex; //into outline
406  int16_t head_index; //start of fragment
407  ICOORD head_pos; //start of fragment
408  int16_t tail_index; //end of fragment
409  ICOORD tail_pos; //end of fragment
410  ICOORD pos; //current point
411  int16_t first_index = 0; //first tail
412  ICOORD first_pos; //first tail
413 
414  length = srcline->pathlength ();
415  pos = srcline->start_pos ();
416  left_edge = pos.x ();
417  tail_index = 0;
418  tail_pos = pos;
419  for (stepindex = 0; stepindex < length; stepindex++) {
420  if (pos.x () < left_edge) {
421  left_edge = pos.x ();
422  tail_index = stepindex;
423  tail_pos = pos;
424  }
425  pos += srcline->step (stepindex);
426  }
427  if (left_edge >= chop_coord - pitch_error)
428  return false; //not worth it
429 
430  startindex = tail_index;
431  first_frag = true;
432  head_index = tail_index;
433  head_pos = tail_pos;
434  do {
435  do {
436  tail_pos += srcline->step (tail_index);
437  tail_index++;
438  if (tail_index == length)
439  tail_index = 0;
440  }
441  while (tail_pos.x () != chop_coord && tail_index != startindex);
442  if (tail_index == startindex) {
443  if (first_frag)
444  return false; //doesn't cross line
445  else
446  break;
447  }
448  ASSERT_HOST (head_index != tail_index);
449  if (!first_frag) {
450  save_chop_cfragment(head_index,
451  head_pos,
452  tail_index,
453  tail_pos,
454  srcline,
455  left_frags);
456  }
457  else {
458  first_index = tail_index;
459  first_pos = tail_pos;
460  first_frag = false;
461  }
462  while (srcline->step (tail_index).x () == 0) {
463  tail_pos += srcline->step (tail_index);
464  tail_index++;
465  if (tail_index == length)
466  tail_index = 0;
467  }
468  head_index = tail_index;
469  head_pos = tail_pos;
470  while (srcline->step (tail_index).x () > 0) {
471  do {
472  tail_pos += srcline->step (tail_index);
473  tail_index++;
474  if (tail_index == length)
475  tail_index = 0;
476  }
477  while (tail_pos.x () != chop_coord);
478  ASSERT_HOST (head_index != tail_index);
479  save_chop_cfragment(head_index,
480  head_pos,
481  tail_index,
482  tail_pos,
483  srcline,
484  right_frags);
485  while (srcline->step (tail_index).x () == 0) {
486  tail_pos += srcline->step (tail_index);
487  tail_index++;
488  if (tail_index == length)
489  tail_index = 0;
490  }
491  head_index = tail_index;
492  head_pos = tail_pos;
493  }
494  }
495  while (tail_index != startindex);
496  save_chop_cfragment(head_index,
497  head_pos,
498  first_index,
499  first_pos,
500  srcline,
501  left_frags);
502  return true; //did some chopping
503 }
504 
505 /**********************************************************************
506  * save_chop_cfragment
507  *
508  * Store the given fragment in the given fragment list.
509  **********************************************************************/
510 
511 void save_chop_cfragment( //chop the outline
512  int16_t head_index, //head of fragment
513  ICOORD head_pos, //head of fragment
514  int16_t tail_index, //tail of fragment
515  ICOORD tail_pos, //tail of fragment
516  C_OUTLINE *srcline, //source of edgesteps
517  C_OUTLINE_FRAG_LIST *frags //fragment list
518  ) {
519  int16_t jump; //gap across end
520  int16_t stepcount; //total steps
521  C_OUTLINE_FRAG *head; //head of fragment
522  C_OUTLINE_FRAG *tail; //tail of fragment
523  int16_t tail_y; //ycoord of tail
524 
525  ASSERT_HOST (tail_pos.x () == head_pos.x ());
526  ASSERT_HOST (tail_index != head_index);
527  stepcount = tail_index - head_index;
528  if (stepcount < 0)
529  stepcount += srcline->pathlength ();
530  jump = tail_pos.y () - head_pos.y ();
531  if (jump < 0)
532  jump = -jump;
533  if (jump == stepcount)
534  return; //its a nop
535  tail_y = tail_pos.y ();
536  head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline,
537  head_index, tail_index);
538  tail = new C_OUTLINE_FRAG (head, tail_y);
539  head->other_end = tail;
540  add_frag_to_list(head, frags);
541  add_frag_to_list(tail, frags);
542 }
543 
544 
545 /**********************************************************************
546  * C_OUTLINE_FRAG::C_OUTLINE_FRAG
547  *
548  * Constructors for C_OUTLINE_FRAG.
549  **********************************************************************/
550 
551 C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment
552  ICOORD start_pt, //start coord
553  ICOORD end_pt, //end coord
554  C_OUTLINE *outline, //source of steps
555  int16_t start_index,
556  int16_t end_index) {
557  start = start_pt;
558  end = end_pt;
559  ycoord = start_pt.y ();
560  stepcount = end_index - start_index;
561  if (stepcount < 0)
562  stepcount += outline->pathlength ();
563  ASSERT_HOST (stepcount > 0);
564  steps = new DIR128[stepcount];
565  if (end_index > start_index) {
566  for (int i = start_index; i < end_index; ++i)
567  steps[i - start_index] = outline->step_dir(i);
568  }
569  else {
570  int len = outline->pathlength();
571  int i = start_index;
572  for (; i < len; ++i)
573  steps[i - start_index] = outline->step_dir(i);
574  if (end_index > 0)
575  for (; i < end_index + len; ++i)
576  steps[i - start_index] = outline->step_dir(i - len);
577  }
578  other_end = nullptr;
579  delete close();
580 }
581 
582 
583 C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment
584  C_OUTLINE_FRAG *head, //other end
585  int16_t tail_y) {
586  ycoord = tail_y;
587  other_end = head;
588  start = head->start;
589  end = head->end;
590  steps = nullptr;
591  stepcount = 0;
592 }
593 
594 
595 /**********************************************************************
596  * add_frag_to_list
597  *
598  * Insert the fragment in the list at the appropriate place to keep
599  * them in ascending ycoord order.
600  **********************************************************************/
601 
602 void add_frag_to_list( //ordered add
603  C_OUTLINE_FRAG *frag, //fragment to add
604  C_OUTLINE_FRAG_LIST *frags //fragment list
605  ) {
606  //output list
607  C_OUTLINE_FRAG_IT frag_it = frags;
608 
609  if (!frags->empty ()) {
610  for (frag_it.mark_cycle_pt (); !frag_it.cycled_list ();
611  frag_it.forward ()) {
612  if (frag_it.data ()->ycoord > frag->ycoord
613  || (frag_it.data ()->ycoord == frag->ycoord
614  && frag->other_end->ycoord < frag->ycoord)) {
615  frag_it.add_before_then_move (frag);
616  return;
617  }
618  }
619  }
620  frag_it.add_to_end (frag);
621 }
622 
623 
624 /**********************************************************************
625  * close_chopped_cfragments
626  *
627  * Clear the given list of fragments joining them up into outlines.
628  * Each outline made soaks up any of the child outlines which it encloses.
629  **********************************************************************/
630 
631 void close_chopped_cfragments( //chop the outline
632  C_OUTLINE_FRAG_LIST *frags, //list to clear
633  C_OUTLINE_LIST *children, //potential children
634  float pitch_error, //allowed shrinkage
635  C_OUTLINE_IT *dest_it //output list
636  ) {
637  //iterator
638  C_OUTLINE_FRAG_IT frag_it = frags;
639  C_OUTLINE_FRAG *bottom_frag; //bottom of cut
640  C_OUTLINE_FRAG *top_frag; //top of cut
641  C_OUTLINE *outline; //new outline
642  C_OUTLINE *child; //current child
643  C_OUTLINE_IT child_it = children;
644  C_OUTLINE_IT olchild_it; //children of outline
645 
646  while (!frag_it.empty()) {
647  frag_it.move_to_first();
648  // get bottom one
649  bottom_frag = frag_it.extract();
650  frag_it.forward();
651  top_frag = frag_it.data(); // look at next
652  if ((bottom_frag->steps == nullptr && top_frag->steps == nullptr)
653  || (bottom_frag->steps != nullptr && top_frag->steps != nullptr)) {
654  if (frag_it.data_relative(1)->ycoord == top_frag->ycoord)
655  frag_it.forward();
656  }
657  top_frag = frag_it.extract();
658  if (top_frag->other_end != bottom_frag) {
659  outline = join_chopped_fragments(bottom_frag, top_frag);
660  ASSERT_HOST(outline == nullptr);
661  } else {
662  outline = join_chopped_fragments(bottom_frag, top_frag);
663  if (outline != nullptr) {
664  olchild_it.set_to_list(outline->child());
665  for (child_it.mark_cycle_pt(); !child_it.cycled_list();
666  child_it.forward()) {
667  child = child_it.data();
668  if (*child < *outline)
669  olchild_it.add_to_end(child_it.extract());
670  }
671  if (outline->bounding_box().width() > pitch_error)
672  dest_it->add_after_then_move(outline);
673  else
674  delete outline; // Make it disappear.
675  }
676  }
677  }
678  while (!child_it.empty ()) {
679  dest_it->add_after_then_move (child_it.extract ());
680  child_it.forward ();
681  }
682 }
683 
684 
685 /**********************************************************************
686  * join_chopped_fragments
687  *
688  * Join the two lists of POLYPTs such that neither OUTLINE_FRAG
689  * operand keeps responsibility for the fragment.
690  **********************************************************************/
691 
692 C_OUTLINE *join_chopped_fragments( //join pieces
693  C_OUTLINE_FRAG *bottom, //bottom of cut
694  C_OUTLINE_FRAG *top //top of cut
695  ) {
696  C_OUTLINE *outline; //closed loop
697 
698  if (bottom->other_end == top) {
699  if (bottom->steps == nullptr)
700  outline = top->close (); //turn to outline
701  else
702  outline = bottom->close ();
703  delete top;
704  delete bottom;
705  return outline;
706  }
707  if (bottom->steps == nullptr) {
708  ASSERT_HOST (top->steps != nullptr);
709  join_segments (bottom->other_end, top);
710  }
711  else {
712  ASSERT_HOST (top->steps == nullptr);
713  join_segments (top->other_end, bottom);
714  }
715  top->other_end->other_end = bottom->other_end;
716  bottom->other_end->other_end = top->other_end;
717  delete bottom;
718  delete top;
719  return nullptr;
720 }
721 
722 /**********************************************************************
723  * join_segments
724  *
725  * Join the two edgestep fragments such that the second comes after
726  * the first and the gap between them is closed.
727  **********************************************************************/
728 
729 void join_segments( //join pieces
730  C_OUTLINE_FRAG *bottom, //bottom of cut
731  C_OUTLINE_FRAG *top //top of cut
732  ) {
733  DIR128 *steps; //new steps
734  int32_t stepcount; //no of steps
735  int16_t fake_count; //fake steps
736  DIR128 fake_step; //step entry
737 
738  ASSERT_HOST (bottom->end.x () == top->start.x ());
739  fake_count = top->start.y () - bottom->end.y ();
740  if (fake_count < 0) {
741  fake_count = -fake_count;
742  fake_step = 32;
743  }
744  else
745  fake_step = 96;
746 
747  stepcount = bottom->stepcount + fake_count + top->stepcount;
748  steps = new DIR128[stepcount];
749  memmove (steps, bottom->steps, bottom->stepcount);
750  memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count);
751  memmove (steps + bottom->stepcount + fake_count, top->steps,
752  top->stepcount);
753  delete [] bottom->steps;
754  bottom->steps = steps;
755  bottom->stepcount = stepcount;
756  bottom->end = top->end;
757  bottom->other_end->end = top->end;
758 }
759 
760 
761 /**********************************************************************
762  * C_OUTLINE_FRAG::close
763  *
764  * Join the ends of this fragment and turn it into an outline.
765  **********************************************************************/
766 
767 C_OUTLINE *C_OUTLINE_FRAG::close() { //join pieces
768  DIR128 *new_steps; //new steps
769  int32_t new_stepcount; //no of steps
770  int16_t fake_count; //fake steps
771  DIR128 fake_step; //step entry
772 
773  ASSERT_HOST (start.x () == end.x ());
774  fake_count = start.y () - end.y ();
775  if (fake_count < 0) {
776  fake_count = -fake_count;
777  fake_step = 32;
778  }
779  else
780  fake_step = 96;
781 
782  new_stepcount = stepcount + fake_count;
783  if (new_stepcount > C_OUTLINE::kMaxOutlineLength)
784  return nullptr; // Can't join them
785  new_steps = new DIR128[new_stepcount];
786  memmove(new_steps, steps, stepcount);
787  memset (new_steps + stepcount, fake_step.get_dir(), fake_count);
788  auto* result = new C_OUTLINE (start, new_steps, new_stepcount);
789  delete [] new_steps;
790  return result;
791 }
792 
793 
794 /**********************************************************************
795  * C_OUTLINE_FRAG::operator=
796  *
797  * Copy this fragment.
798  **********************************************************************/
799 
800  //join pieces
802 const C_OUTLINE_FRAG & src //fragment to copy
803 ) {
804  delete [] steps;
805 
806  stepcount = src.stepcount;
807  steps = new DIR128[stepcount];
808  memmove (steps, src.steps, stepcount);
809  start = src.start;
810  end = src.end;
811  ycoord = src.ycoord;
812  return *this;
813 }
INT_VAR
#define INT_VAR(name, val, comment)
Definition: params.h:300
split_to_blob
void split_to_blob(BLOBNBOX *blob, int16_t chop_coord, float pitch_error, C_OUTLINE_LIST *left_coutlines, C_OUTLINE_LIST *right_coutlines)
Definition: fpchop.cpp:232
C_BLOB::out_list
C_OUTLINE_LIST * out_list()
Definition: stepblob.h:69
W_REP_CHAR
repeated character
Definition: werd.h:52
DIR128
Definition: mod128.h:28
W_DONT_CHOP
fixed pitch chopped
Definition: werd.h:51
ASSERT_HOST
#define ASSERT_HOST(x)
Definition: errcode.h:87
C_OUTLINE_FRAG::end
ICOORD end
Definition: fpchop.h:62
WERD::bounding_box
TBOX bounding_box() const
Definition: werd.cpp:147
C_OUTLINE_FRAG
Definition: fpchop.h:24
blobbox.h
ICOORD
integer coordinate
Definition: points.h:30
C_OUTLINE_FRAG::start
ICOORD start
Definition: fpchop.h:61
ROW::recalc_bounding_box
void recalc_bounding_box()
Definition: ocrrow.cpp:96
fixed_split_coutline
void fixed_split_coutline(C_OUTLINE *srcline, int16_t chop_coord, float pitch_error, C_OUTLINE_IT *left_it, C_OUTLINE_IT *right_it)
Definition: fpchop.cpp:306
plot_row_cells
void plot_row_cells(ScrollView *win, ScrollView::Color colour, TO_ROW *row, float xshift, ICOORDELT_LIST *cells)
Definition: drawtord.cpp:383
textord_fp_chop_error
int textord_fp_chop_error
Definition: fpchop.cpp:31
ICOORD::x
int16_t x() const
access function
Definition: points.h:51
FCOORD
Definition: points.h:187
C_OUTLINE
class DLLSYM C_OUTLINE
Definition: coutln.h:67
BLOBNBOX
Definition: blobbox.h:142
C_OUTLINE_FRAG::other_end
C_OUTLINE_FRAG * other_end
Definition: fpchop.h:65
C_BLOB
Definition: stepblob.h:36
C_OUTLINE
Definition: coutln.h:71
statistc.h
tovars.h
topitch.h
textord_show_page_cuts
bool textord_show_page_cuts
Definition: topitch.cpp:46
close_chopped_cfragments
void close_chopped_cfragments(C_OUTLINE_FRAG_LIST *frags, C_OUTLINE_LIST *children, float pitch_error, C_OUTLINE_IT *dest_it)
Definition: fpchop.cpp:620
WERD::set_flag
void set_flag(WERD_FLAGS mask, bool value)
Definition: werd.h:117
C_OUTLINE_FRAG::steps
DIR128 * steps
Definition: fpchop.h:63
C_OUTLINE::step_dir
DIR128 step_dir(int index) const
Definition: coutln.h:138
W_EOL
end of line
Definition: werd.h:47
C_OUTLINE_FRAG::operator=
C_OUTLINE_FRAG & operator=(const C_OUTLINE_FRAG &src)
Definition: fpchop.cpp:786
TBOX::width
int16_t width() const
Definition: rect.h:114
textord_fp_chop_snap
double textord_fp_chop_snap
Definition: fpchop.cpp:33
join_chopped_fragments
C_OUTLINE * join_chopped_fragments(C_OUTLINE_FRAG *bottom, C_OUTLINE_FRAG *top)
Definition: fpchop.cpp:680
save_chop_cfragment
void save_chop_cfragment(int16_t head_index, ICOORD head_pos, int16_t tail_index, ICOORD tail_pos, C_OUTLINE *srcline, C_OUTLINE_FRAG_LIST *frags)
Definition: fpchop.cpp:503
double_VAR
#define double_VAR(name, val, comment)
Definition: params.h:309
join_segments
void join_segments(C_OUTLINE_FRAG *bottom, C_OUTLINE_FRAG *top)
Definition: fpchop.cpp:716
ScrollView::RED
Definition: scrollview.h:104
drawtord.h
fixed_pitch_words
ROW * fixed_pitch_words(TO_ROW *row, FCOORD rotation)
Definition: fpchop.cpp:42
C_OUTLINE_FRAG::ycoord
int16_t ycoord
Definition: fpchop.h:66
C_OUTLINE_FRAG::C_OUTLINE_FRAG
C_OUTLINE_FRAG()
Definition: fpchop.h:42
C_OUTLINE_FRAG::stepcount
int32_t stepcount
Definition: fpchop.h:64
add_frag_to_list
void add_frag_to_list(C_OUTLINE_FRAG *frag, C_OUTLINE_FRAG_LIST *frags)
Definition: fpchop.cpp:592
WERD
Definition: werd.h:55
TBOX::left
int16_t left() const
Definition: rect.h:71
ROW
Definition: ocrrow.h:35
C_OUTLINE::bounding_box
const TBOX & bounding_box() const
Definition: coutln.h:112
DIR128::get_dir
int8_t get_dir() const
Definition: mod128.h:75
TBOX::right
int16_t right() const
Definition: rect.h:78
C_OUTLINE::kMaxOutlineLength
static const int kMaxOutlineLength
Definition: coutln.h:272
tprintf
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:34
WERD::set_blanks
void set_blanks(uint8_t new_blanks)
Definition: werd.h:101
TO_ROW
Definition: blobbox.h:543
C_OUTLINE::pathlength
int32_t pathlength() const
Definition: coutln.h:134
C_OUTLINE::step
ICOORD step(int index) const
Definition: coutln.h:143
BLOBNBOX::cblob
C_BLOB * cblob() const
Definition: blobbox.h:267
C_OUTLINE_FRAG::close
C_OUTLINE * close()
Definition: fpchop.cpp:753
add_repeated_word
WERD * add_repeated_word(WERD_IT *rep_it, int16_t &rep_left, int16_t &prev_chop_coord, uint8_t &blanks, float pitch, WERD_IT *word_it)
Definition: fpchop.cpp:197
C_OUTLINE::child
C_OUTLINE_LIST * child()
Definition: coutln.h:107
fixed_chop_coutline
bool fixed_chop_coutline(C_OUTLINE *srcline, int16_t chop_coord, float pitch_error, C_OUTLINE_FRAG_LIST *left_frags, C_OUTLINE_FRAG_LIST *right_frags)
Definition: fpchop.cpp:387
ROW::word_list
WERD_LIST * word_list()
Definition: ocrrow.h:54
fixed_chop_cblob
void fixed_chop_cblob(C_BLOB *blob, int16_t chop_coord, float pitch_error, C_OUTLINE_LIST *left_outlines, C_OUTLINE_LIST *right_outlines)
Definition: fpchop.cpp:261
to_win
ScrollView * to_win
Definition: drawtord.cpp:34
fpchop.h
ELISTIZE
#define ELISTIZE(CLASSNAME)
Definition: elst.h:919
C_OUTLINE::start_pos
const ICOORD & start_pos() const
Definition: coutln.h:147
W_BOL
start of line
Definition: werd.h:46
ICOORD::y
int16_t y() const
access_function
Definition: points.h:55
TBOX
Definition: rect.h:33