30 #include "config_auto.h"
87 "Fraction of height used as a minimum gap for aligned blobs.");
90 TabVector_LIST* vlines,
int vertical_x,
int vertical_y,
93 resolution_(resolution),
94 image_origin_(0, tright.y() - 1) {
96 v_it_.set_to_list(&vectors_);
97 v_it_.add_list_after(vlines);
98 SetVerticalSkewAndParellelize(vertical_x, vertical_y);
103 if (width_cb_ !=
NULL)
117 BLOBNBOX_LIST* blobs,
119 BLOBNBOX_C_IT>* grid) {
120 BLOBNBOX_IT blob_it(blobs);
122 int reject_count = 0;
123 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
126 if (
InsertBlob(h_spread, v_spread, blob, grid)) {
133 tprintf(
"Inserted %d blobs into grid, %d rejected.\n",
134 b_count, reject_count);
145 BLOBNBOX_C_IT>* grid) {
153 grid->InsertBBox(h_spread, v_spread, blob);
168 BLOBNBOX_IT blob_it(blobs);
169 for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
187 bool ignore_unmergeables,
int max_gutter_width,
188 int* required_shift) {
190 int bottom_x = v.
XAtY(bottom_y);
191 int top_x = v.
XAtY(top_y);
192 int start_x = right_to_left ?
MAX(top_x, bottom_x) :
MIN(top_x, bottom_x);
195 int min_gap = max_gutter_width;
200 if (box.
bottom() >= top_y || box.
top() <= bottom_y)
209 int mid_y = (box.
bottom() + box.
top()) / 2;
214 int tab_x = v.
XAtY(mid_y);
217 gap = tab_x - box.
right();
218 if (gap < 0 && box.
left() - tab_x < *required_shift)
219 *required_shift = box.
left() - tab_x;
221 gap = box.
left() - tab_x;
222 if (gap < 0 && box.
right() - tab_x > *required_shift)
223 *required_shift = box.
right() - tab_x;
225 if (gap > 0 && gap < min_gap)
229 return min_gap - abs(*required_shift);
234 int max_gutter,
bool left,
236 int* neighbour_gap ) {
239 int gutter_x = left ? box.
left() : box.
right();
240 int internal_x = left ? box.
right() : box.
left();
242 int tab_gap = left ? gutter_x - tab_x : tab_x - gutter_x;
243 *gutter_width = max_gutter;
247 *gutter_width += tab_gap;
250 tprintf(
"Looking in gutter\n");
252 BLOBNBOX* gutter_bbox = AdjacentBlob(bbox, left,
255 if (gutter_bbox !=
NULL) {
257 *gutter_width = left ? tab_x - gutter_box.
right()
258 : gutter_box.
left() - tab_x;
260 if (*gutter_width >= max_gutter) {
262 TBOX gutter_box(box);
264 gutter_box.
set_left(tab_x - max_gutter - 1);
265 gutter_box.
set_right(tab_x - max_gutter);
267 if (tab_gutter < tab_x - 1)
268 *gutter_width = tab_x - tab_gutter;
270 gutter_box.
set_left(tab_x + max_gutter);
271 gutter_box.
set_right(tab_x + max_gutter + 1);
273 if (tab_gutter > tab_x + 1)
274 *gutter_width = tab_gutter - tab_x;
277 if (*gutter_width > max_gutter)
278 *gutter_width = max_gutter;
281 tprintf(
"Looking for neighbour\n");
282 BLOBNBOX* neighbour = AdjacentBlob(bbox, !left,
287 if (neighbour !=
NULL) {
293 if (left && n_box.
left() < neighbour_edge)
294 neighbour_edge = n_box.
left();
295 else if (!left && n_box.
right() > neighbour_edge)
296 neighbour_edge = n_box.
right();
298 *neighbour_gap = left ? neighbour_edge - internal_x
299 : internal_x - neighbour_edge;
333 int top_y = box.
top();
334 int bottom_y = box.
bottom();
335 int mid_y = (top_y + bottom_y) / 2;
336 int right = crossing ? (box.
left() + box.
right()) / 2 : box.
right();
337 int min_key, max_key;
340 while (!v_it_.at_first() && v_it_.data()->sort_key() >= min_key)
342 while (!v_it_.at_last() && v_it_.data()->sort_key() < min_key)
350 int x = v->
XAtY(mid_y);
352 (v->
VOverlap(top_y, bottom_y) > 0 ||
354 if (best_v ==
NULL || x < best_x) {
359 key_limit = v->
sort_key() + max_key - min_key;
364 if (v_it_.at_last() ||
368 }
while (!v_it_.at_first());
377 int top_y = box.
top();
378 int bottom_y = box.
bottom();
379 int mid_y = (top_y + bottom_y) / 2;
380 int left = crossing ? (box.
left() + box.
right()) / 2 : box.
left();
381 int min_key, max_key;
384 while (!v_it_.at_last() && v_it_.data()->sort_key() <= max_key)
386 while (!v_it_.at_first() && v_it_.data()->sort_key() > max_key) {
395 int x = v->
XAtY(mid_y);
397 (v->
VOverlap(top_y, bottom_y) > 0 ||
399 if (best_v ==
NULL || x > best_x) {
404 key_limit = v->
sort_key() - (max_key - min_key);
409 if (v_it_.at_first() ||
413 }
while (!v_it_.at_last());
421 ICOORDELT_IT it(&column_widths_);
422 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
424 if (NearlyEqual<int>(width, w->
x(), 1))
433 return size1 > size2 * 2 || size2 > size1 * 2;
439 return size1 > size2 * 5 || size2 > size1 * 5;
448 BLOBNBOX_LIST* image_blobs,
TO_BLOCK* block,
449 int min_gutter_width,
454 ComputeColumnWidths(tab_win, part_grid);
458 if (!Deskew(hlines, image_blobs, block, deskew, reskew))
460 part_grid->
Deskew(*deskew);
461 ApplyTabConstraints();
462 #ifndef GRAPHICS_DISABLED
467 image_origin_.
x(), image_origin_.
y());
474 #endif // GRAPHICS_DISABLED
495 BLOBNBOX_IT blob_it = &block->
blobs;
497 for (large_it.mark_cycle_pt(); !large_it.cycled_list(); large_it.forward()) {
498 BLOBNBOX* large_blob = large_it.data();
500 blob_it.add_to_end(large_it.extract());
505 tprintf(
"Moved %d large blobs to normal list\n",
507 #ifndef GRAPHICS_DISABLED
512 #endif // GRAPHICS_DISABLED
521 *min_key =
MIN(key1, key2);
522 *max_key =
MAX(key1, key2);
526 #ifndef GRAPHICS_DISABLED
528 TabVector_IT it(&vectors_);
529 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
543 int min_gutter_width,
550 if (image_blobs !=
NULL)
553 ScrollView* initial_win = FindTabBoxes(min_gutter_width);
554 FindAllTabVectors(min_gutter_width);
568 #ifndef GRAPHICS_DISABLED
569 for (
int i = 0; i < boxes.
size(); ++i) {
570 TBOX box = boxes[i]->bounding_box();
571 int left_x = box.
left();
572 int right_x = box.
right();
573 int top_y = box.
top();
574 int bottom_y = box.
bottom();
577 win->
Rectangle(left_x, bottom_y, right_x, top_y);
580 #endif // GRAPHICS_DISABLED
585 ScrollView* TabFind::FindTabBoxes(
int min_gutter_width) {
586 left_tab_boxes_.
clear();
587 right_tab_boxes_.
clear();
589 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> gsearch(
this);
590 gsearch.StartFullSearch();
592 while ((bbox = gsearch.NextFullSearch()) !=
NULL) {
593 if (TestBoxForTabs(bbox, min_gutter_width)) {
603 left_tab_boxes_.
sort(SortByBoxLeft<BLOBNBOX>);
604 right_tab_boxes_.
sort(SortRightToLeft<BLOBNBOX>);
606 #ifndef GRAPHICS_DISABLED
612 DisplayBoxVector(left_tab_boxes_, tab_win);
613 DisplayBoxVector(right_tab_boxes_, tab_win);
616 #endif // GRAPHICS_DISABLED
620 bool TabFind::TestBoxForTabs(
BLOBNBOX* bbox,
int min_gutter_width) {
621 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> radsearch(
this);
624 int left_column_edge = bbox->
left_rule();
627 int left_x = box.
left();
628 int right_x = box.
right();
629 int top_y = box.
top();
630 int bottom_y = box.
bottom();
631 int height = box.
height();
634 tprintf(
"Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
635 left_x, top_y, right_x, bottom_y,
636 left_column_edge, right_column_edge);
640 radsearch.StartRadSearch((left_x + right_x)/2, (top_y + bottom_y)/2, radius);
647 if (min_gutter_width > min_spacing)
648 min_spacing = min_gutter_width;
650 if (min_gutter_width > min_ragged_gutter)
651 min_ragged_gutter = min_gutter_width;
652 int target_right = left_x - min_spacing;
653 int target_left = right_x + min_spacing;
669 bool is_left_tab =
true;
670 bool is_right_tab =
true;
671 bool maybe_ragged_left =
true;
672 bool maybe_ragged_right =
true;
673 int maybe_left_tab_up = 0;
674 int maybe_right_tab_up = 0;
675 int maybe_left_tab_down = 0;
676 int maybe_right_tab_down = 0;
679 maybe_ragged_left =
false;
684 is_right_tab =
false;
685 maybe_ragged_right =
false;
691 while ((neighbour = radsearch.NextRadSearch()) !=
NULL) {
692 if (neighbour == bbox)
695 int n_left = nbox.
left();
696 int n_right = nbox.
right();
698 tprintf(
"Neighbour at (%d,%d)->(%d,%d)\n",
699 n_left, nbox.
bottom(), n_right, nbox.
top());
702 if (n_right > right_column_edge || n_left < left_column_edge ||
703 left_x < neighbour->left_rule() || right_x > neighbour->
right_rule())
705 int n_mid_x = (n_left + n_right) / 2;
706 int n_mid_y = (nbox.
top() + nbox.
bottom()) / 2;
707 if (n_mid_x <= left_x && n_right >= target_right) {
713 if (n_mid_y > bottom_y)
715 }
else if (
NearlyEqual(left_x, n_left, alignment_tolerance)) {
718 if (n_mid_y > top_y && maybe_left_tab_up > -
MAX_INT32)
720 if (n_mid_y < bottom_y && maybe_left_tab_down > -
MAX_INT32)
721 ++maybe_left_tab_down;
722 }
else if (n_left < left_x && n_right >= left_x) {
725 tprintf(
"Maybe Not a left tab\n");
726 if (n_mid_y > top_y && maybe_left_tab_up > -
MAX_INT32)
728 if (n_mid_y < bottom_y && maybe_left_tab_down > -
MAX_INT32)
729 --maybe_left_tab_down;
731 if (n_left < left_x && nbox.
y_overlap(box) && n_right >= target_right) {
732 maybe_ragged_left =
false;
734 tprintf(
"Not a ragged left\n");
736 if (n_mid_x >= right_x && n_left <= target_left) {
739 is_right_tab =
false;
742 if (n_mid_y > bottom_y)
744 }
else if (
NearlyEqual(right_x, n_right, alignment_tolerance)) {
746 tprintf(
"Maybe a right tab\n");
747 if (n_mid_y > top_y && maybe_right_tab_up > -
MAX_INT32)
748 ++maybe_right_tab_up;
749 if (n_mid_y < bottom_y && maybe_right_tab_down > -
MAX_INT32)
750 ++maybe_right_tab_down;
751 }
else if (n_right > right_x && n_left <= right_x) {
754 tprintf(
"Maybe Not a right tab\n");
755 if (n_mid_y > top_y && maybe_right_tab_up > -
MAX_INT32)
756 --maybe_right_tab_up;
757 if (n_mid_y < bottom_y && maybe_right_tab_down > -
MAX_INT32)
758 --maybe_right_tab_down;
760 if (n_right > right_x && nbox.
y_overlap(box) && n_left <= target_left) {
761 maybe_ragged_right =
false;
763 tprintf(
"Not a ragged right\n");
769 if (is_left_tab || maybe_left_tab_up > 1 || maybe_left_tab_down > 1) {
771 }
else if (maybe_ragged_left && ConfirmRaggedLeft(bbox, min_ragged_gutter)) {
776 if (is_right_tab || maybe_right_tab_up > 1 || maybe_right_tab_down > 1) {
778 }
else if (maybe_ragged_right &&
779 ConfirmRaggedRight(bbox, min_ragged_gutter)) {
785 tprintf(
"Left result = %s, Right result=%s\n",
796 bool TabFind::ConfirmRaggedLeft(
BLOBNBOX* bbox,
int min_gutter) {
799 search_box.set_left(search_box.left() - min_gutter);
800 return NothingYOverlapsInBox(search_box, bbox->
bounding_box());
805 bool TabFind::ConfirmRaggedRight(
BLOBNBOX* bbox,
int min_gutter) {
807 search_box.
set_left(search_box.right());
808 search_box.set_right(search_box.right() + min_gutter);
809 return NothingYOverlapsInBox(search_box, bbox->
bounding_box());
814 bool TabFind::NothingYOverlapsInBox(
const TBOX& search_box,
815 const TBOX& target_box) {
817 rsearch.StartRectSearch(search_box);
819 while ((blob = rsearch.NextRectSearch()) !=
NULL) {
821 if (box.
y_overlap(target_box) && !(box == target_box))
827 void TabFind::FindAllTabVectors(
int min_gutter_width) {
829 TabVector_LIST dummy_vectors;
840 &vertical_x, &vertical_y);
844 &vertical_x, &vertical_y);
845 if (vector_count > 0)
849 dummy_vectors.clear();
850 for (
int i = 0; i < left_tab_boxes_.
size(); ++i) {
851 BLOBNBOX* bbox = left_tab_boxes_[i];
855 for (
int i = 0; i < right_tab_boxes_.
size(); ++i) {
856 BLOBNBOX* bbox = right_tab_boxes_[i];
861 tprintf(
"Beginning real tab search with vertical = %d,%d...\n",
862 vertical_x, vertical_y);
868 &dummy_vectors, &vertical_x, &vertical_y);
870 &dummy_vectors, &vertical_x, &vertical_y);
872 &dummy_vectors, &vertical_x, &vertical_y);
874 &dummy_vectors, &vertical_x, &vertical_y);
876 TabVector_IT v_it(&vectors_);
877 v_it.add_list_after(&dummy_vectors);
879 SetVerticalSkewAndParellelize(vertical_x, vertical_y);
884 int min_gutter_width, TabVector_LIST* vectors,
885 int* vertical_x,
int* vertical_y) {
886 TabVector_IT vector_it(vectors);
887 int vector_count = 0;
892 for (
int i = 0; i < boxes.
size(); ++i) {
896 TabVector* vector = FindTabVector(search_size_multiple, min_gutter_width,
898 bbox, vertical_x, vertical_y);
899 if (vector !=
NULL) {
901 vector_it.add_to_end(vector);
915 TabVector* TabFind::FindTabVector(
int search_size_multiple,
916 int min_gutter_width,
919 int* vertical_x,
int* vertical_y) {
921 AlignedBlobParams align_params(*vertical_x, *vertical_y,
923 search_size_multiple, min_gutter_width,
931 void TabFind::SetVerticalSkewAndParellelize(
int vertical_x,
int vertical_y) {
935 tprintf(
"Vertical skew vector=(%d,%d)\n",
937 v_it_.set_to_list(&vectors_);
938 for (v_it_.mark_cycle_pt(); !v_it_.cycled_list(); v_it_.forward()) {
939 TabVector* v = v_it_.data();
947 void TabFind::SortVectors() {
949 v_it_.set_to_list(&vectors_);
953 void TabFind::EvaluateTabs() {
954 TabVector_IT rule_it(&vectors_);
955 for (rule_it.mark_cycle_pt(); !rule_it.cycled_list(); rule_it.forward()) {
956 TabVector* tab = rule_it.data();
957 if (!tab->IsSeparator()) {
961 tab->Print(
"Too few boxes");
962 delete rule_it.extract();
963 v_it_.set_to_list(&vectors_);
965 tab->Print(
"Evaluated tab");
974 void TabFind::ComputeColumnWidths(
ScrollView* tab_win,
975 ColPartitionGrid* part_grid) {
976 #ifndef GRAPHICS_DISABLED
979 #endif // GRAPHICS_DISABLED
982 STATS col_widths(0, col_widths_size + 1);
983 ApplyPartitionsToColumnWidths(part_grid, &col_widths);
984 #ifndef GRAPHICS_DISABLED
985 if (tab_win !=
NULL) {
988 #endif // GRAPHICS_DISABLED
992 MakeColumnWidths(col_widths_size, &col_widths);
996 void TabFind::ApplyPartitionsToColumnWidths(ColPartitionGrid* part_grid,
1001 gsearch.StartFullSearch();
1003 while ((part = gsearch.NextFullSearch()) !=
NULL) {
1004 BLOBNBOX_C_IT blob_it(part->boxes());
1005 if (blob_it.empty())
1007 BLOBNBOX* left_blob = blob_it.data();
1008 blob_it.move_to_last();
1009 BLOBNBOX* right_blob = blob_it.data();
1012 if (left_vector ==
NULL || left_vector->IsRightTab())
1016 if (right_vector ==
NULL || right_vector->IsLeftTab())
1019 AddPartnerVector(left_blob, right_blob, left_vector, right_vector);
1023 int width = line_right - line_left;
1032 void TabFind::MakeColumnWidths(
int col_widths_size,
STATS* col_widths) {
1033 ICOORDELT_IT w_it(&column_widths_);
1034 int total_col_count = col_widths->
get_total();
1036 int width = col_widths->
mode();
1037 int col_count = col_widths->
pile_count(width);
1038 col_widths->
add(width, -col_count);
1040 for (
int left = width - 1; left > 0 &&
1043 int new_count = col_widths->
pile_count(left);
1044 col_count += new_count;
1045 col_widths->
add(left, -new_count);
1047 for (
int right = width + 1; right < col_widths_size &&
1050 int new_count = col_widths->
pile_count(right);
1051 col_count += new_count;
1052 col_widths->
add(right, -new_count);
1057 w_it.add_after_then_move(w);
1059 tprintf(
"Column of width %d has %d = %.2f%% lines\n",
1061 100.0 * col_count / total_col_count);
1068 void TabFind::MarkVerticalText() {
1070 tprintf(
"Checking for vertical lines\n");
1072 gsearch.StartFullSearch();
1074 while ((blob = gsearch.NextFullSearch()) !=
NULL) {
1083 int TabFind::FindMedianGutterWidth(TabVector_LIST *lines) {
1084 TabVector_IT it(lines);
1085 int prev_right = -1;
1087 STATS gaps(0, max_gap);
1088 STATS heights(0, max_gap);
1089 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1090 TabVector* v = it.data();
1091 TabVector* partner = v->GetSinglePartner();
1092 if (!v->IsLeftTab() || v->IsSeparator() || !partner)
continue;
1093 heights.add(partner->startpt().x() - v->startpt().x(), 1);
1094 if (prev_right > 0 && v->startpt().x() > prev_right) {
1095 gaps.add(v->startpt().x() - prev_right, 1);
1097 prev_right = partner->startpt().x();
1100 tprintf(
"TabGutter total %d median_gap %.2f median_hgt %.2f\n",
1101 gaps.get_total(), gaps.median(), heights.median());
1103 return static_cast<int>(gaps.median());
1112 bool look_left,
bool ignore_images,
1113 double min_overlap_fraction,
1114 int gap_limit,
int top_y,
int bottom_y) {
1115 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> sidesearch(
this);
1117 int left = box.
left();
1118 int right = box.
right();
1119 int mid_x = (left + right) / 2;
1120 sidesearch.StartSideSearch(mid_x, bottom_y, top_y);
1125 while ((neighbour = sidesearch.NextSideSearch(look_left)) !=
NULL) {
1127 tprintf(
"Adjacent blob: considering box:");
1130 if (neighbour == bbox ||
1134 int n_top_y = nbox.
top();
1135 int n_bottom_y = nbox.
bottom();
1136 int v_overlap =
MIN(n_top_y, top_y) -
MAX(n_bottom_y, bottom_y);
1137 int height = top_y - bottom_y;
1138 int n_height = n_top_y - n_bottom_y;
1139 if (v_overlap > min_overlap_fraction *
MIN(height, n_height) &&
1140 (min_overlap_fraction == 0.0 || !
DifferentSizes(height, n_height))) {
1141 int n_left = nbox.
left();
1142 int n_right = nbox.
right();
1143 int h_gap =
MAX(n_left, left) -
MIN(n_right, right);
1144 int n_mid_x = (n_left + n_right) / 2;
1145 if (look_left == (n_mid_x < mid_x) && n_mid_x != mid_x) {
1146 if (h_gap > gap_limit) {
1149 tprintf(
"Giving up due to big gap = %d vs %d\n",
1158 tprintf(
"Collision with like tab of type %d at %d,%d\n",
1166 if (result ==
NULL || h_gap < best_gap) {
1179 tprintf(
"Insufficient overlap\n");
1183 tprintf(
"Giving up due to end of search\n");
1192 TabVector* left, TabVector* right) {
1195 if (left->IsSeparator()) {
1198 if (v !=
NULL && v != left && v->IsLeftTab() &&
1199 v->XAtY(left_box.
top()) > left->XAtY(left_box.
top())) {
1201 left->ExtendToBox(left_blob);
1206 v_it_.move_to_first();
1209 if (right->IsSeparator()) {
1212 tprintf(
"Box edge (%d,%d-%d)",
1214 right->Print(
" looking for improvement for");
1217 if (v !=
NULL && v != right && v->IsRightTab() &&
1218 v->XAtY(right_box.
top()) < right->XAtY(right_box.
top())) {
1220 right->ExtendToBox(right_blob);
1222 right->Print(
"Extended vector");
1229 v_it_.move_to_first();
1231 right->Print(
"Created new vector");
1235 left->AddPartner(right);
1236 right->AddPartner(left);
1241 void TabFind::CleanupTabs() {
1245 TabVector_IT it(&vectors_);
1246 TabVector_IT dead_it(&dead_vectors_);
1247 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1248 TabVector* v = it.data();
1249 if (v->IsSeparator() || v->Partnerless()) {
1250 dead_it.add_after_then_move(it.extract());
1251 v_it_.set_to_list(&vectors_);
1260 BLOBNBOX_IT it(blobs);
1261 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1262 it.data()->rotate_box(rotation);
1268 bool TabFind::Deskew(TabVector_LIST* hlines, BLOBNBOX_LIST* image_blobs,
1270 ComputeDeskewVectors(deskew, reskew);
1281 int width = pixGetWidth(pix_grey);
1282 int height = pixGetHeight(pix_grey);
1283 float angle = atan2(deskew->
y(), deskew->
x());
1285 Pix* pix_rot = pixRotate(pix_grey, -angle, L_ROTATE_AREA_MAP,
1286 L_BRING_IN_WHITE, width, height);
1289 ICOORD center_offset(width / 2, height / 2);
1290 ICOORD new_center_offset(center_offset);
1291 new_center_offset.rotate(*deskew);
1292 image_origin_ += new_center_offset - center_offset;
1296 ICOORD corner_offset((width - pixGetWidth(pix_rot)) / 2,
1297 (pixGetHeight(pix_rot) - height) / 2);
1298 image_origin_ += corner_offset;
1300 pixDestroy(&pix_grey);
1301 pixDestroy(&pix_rot);
1306 TabVector_IT h_it(hlines);
1307 for (h_it.mark_cycle_pt(); !h_it.cycled_list(); h_it.forward()) {
1308 TabVector* h = h_it.data();
1311 TabVector_IT d_it(&dead_vectors_);
1312 for (d_it.mark_cycle_pt(); !d_it.cycled_list(); d_it.forward()) {
1313 TabVector* d = d_it.data();
1316 SetVerticalSkewAndParellelize(0, 1);
1319 grid_box.rotate_large(*deskew);
1320 Init(
gridsize(), grid_box.botleft(), grid_box.topright());
1330 TabVector_LIST* horizontal_lines,
1331 int* min_gutter_width) {
1335 TabVector_LIST ex_verticals;
1336 TabVector_IT ex_v_it(&ex_verticals);
1337 TabVector_LIST vlines;
1338 TabVector_IT v_it(&vlines);
1339 while (!v_it_.empty()) {
1343 ex_v_it.add_after_then_move(v);
1345 v_it.add_after_then_move(v);
1352 int median_gutter = FindMedianGutterWidth(&vlines);
1353 if (median_gutter > *min_gutter_width)
1354 *min_gutter_width = median_gutter;
1356 TabVector_IT h_it(horizontal_lines);
1357 for (h_it.mark_cycle_pt(); !h_it.cycled_list(); h_it.forward()) {
1361 v_it_.add_list_after(horizontal_lines);
1362 v_it_.move_to_first();
1363 h_it.set_to_list(horizontal_lines);
1364 h_it.add_list_after(&ex_verticals);
1375 v_it_.move_to_first();
1376 for (v_it_.mark_cycle_pt(); !v_it_.cycled_list(); v_it_.forward()) {
1377 if (!v_it_.data()->IsSeparator())
1378 delete v_it_.extract();
1386 TabVector_LIST temp_list;
1387 TabVector_IT temp_it(&temp_list);
1388 v_it_.move_to_first();
1392 while (!v_it_.empty()) {
1396 temp_it.add_before_then_move(v);
1398 v_it_.add_list_after(&temp_list);
1399 v_it_.move_to_first();
1402 int tmp = grid_box.
left();
1409 void TabFind::ComputeDeskewVectors(
FCOORD* deskew,
FCOORD* reskew) {
1411 length = sqrt(length);
1414 reskew->
set_x(deskew->
x());
1415 reskew->
set_y(-deskew->
y());
1420 void TabFind::ApplyTabConstraints() {
1421 TabVector_IT it(&vectors_);
1422 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1423 TabVector* v = it.data();
1424 v->SetupConstraints();
1426 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1427 TabVector* v = it.data();
1431 v->SetupPartnerConstraints();
1436 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1437 TabVector* v = it.data();
1438 if (!v->IsRightTab())
1441 TabVector_IT partner_it(it);
1442 for (partner_it.forward(); !partner_it.at_first(); partner_it.forward()) {
1443 TabVector* partner = partner_it.data();
1444 if (!partner->IsLeftTab() || !v->VOverlap(*partner))
1446 v->SetupPartnerConstraints(partner);
1450 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
1451 TabVector* v = it.data();
1452 if (!v->IsSeparator())
1453 v->ApplyConstraints();