tesseract 4.1.1
Loading...
Searching...
No Matches
alignedblob.cpp
Go to the documentation of this file.
1
2// File: alignedblob.cpp
3// Description: Subclass of BBGrid to find vertically aligned blobs.
4// Author: Ray Smith
5//
6// (C) Copyright 2008, Google Inc.
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//
18
19#ifdef HAVE_CONFIG_H
20#include "config_auto.h"
21#endif
22
23#include "alignedblob.h"
24
25#include <algorithm>
26
27INT_VAR(textord_debug_tabfind, 0, "Debug tab finding");
28INT_VAR(textord_debug_bugs, 0, "Turn on output related to bugs in tab finding");
29static INT_VAR(textord_testregion_left, -1, "Left edge of debug reporting rectangle");
30static INT_VAR(textord_testregion_top, -1, "Top edge of debug reporting rectangle");
31static INT_VAR(textord_testregion_right, INT32_MAX, "Right edge of debug rectangle");
32static INT_VAR(textord_testregion_bottom, INT32_MAX, "Bottom edge of debug rectangle");
33BOOL_VAR(textord_debug_printable, false, "Make debug windows printable");
34
35namespace tesseract {
36
37// Fraction of resolution used as alignment tolerance for aligned tabs.
38const double kAlignedFraction = 0.03125;
39// Fraction of resolution used as alignment tolerance for ragged tabs.
40const double kRaggedFraction = 2.5;
41// Fraction of height used as a minimum gutter gap for aligned blobs.
42const double kAlignedGapFraction = 0.75;
43// Fraction of height used as a minimum gutter gap for ragged tabs.
44const double kRaggedGapFraction = 1.0;
45// Constant number of pixels used as alignment tolerance for line finding.
46const int kVLineAlignment = 3;
47// Constant number of pixels used as gutter gap tolerance for line finding.
48const int kVLineGutter = 1;
49// Constant number of pixels used as the search size for line finding.
50const int kVLineSearchSize = 150;
51// Min number of points to accept for a ragged tab stop.
52const int kMinRaggedTabs = 5;
53// Min number of points to accept for an aligned tab stop.
54const int kMinAlignedTabs = 4;
55// Constant number of pixels minimum height of a vertical line.
56const int kVLineMinLength = 500;
57// Minimum gradient for a vertical tab vector. Used to prune away junk
58// tab vectors with what would be a ridiculously large skew angle.
59// Value corresponds to tan(90 - max allowed skew angle)
60const double kMinTabGradient = 4.0;
61// Tolerance to skew on top of current estimate of skew. Divide x or y length
62// by kMaxSkewFactor to get the y or x skew distance.
63// If the angle is small, the angle in degrees is roughly 60/kMaxSkewFactor.
64const int kMaxSkewFactor = 15;
65
66// Constructor to set the parameters for finding aligned and ragged tabs.
67// Vertical_x and vertical_y are the current estimates of the true vertical
68// direction (up) in the image. Height is the height of the starter blob.
69// v_gap_multiple is the multiple of height that will be used as a limit
70// on vertical gap before giving up and calling the line ended.
71// resolution is the original image resolution, and align0 indicates the
72// type of tab stop to be found.
73AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
74 int height, int v_gap_multiple,
75 int min_gutter_width,
76 int resolution, TabAlignment align0)
77 : right_tab(align0 == TA_RIGHT_RAGGED || align0 == TA_RIGHT_ALIGNED),
78 ragged(align0 == TA_LEFT_RAGGED || align0 == TA_RIGHT_RAGGED),
79 alignment(align0),
80 confirmed_type(TT_CONFIRMED),
81 min_length(0) {
82 // Set the tolerances according to the type of line sought.
83 // For tab search, these are based on the image resolution for most, or
84 // the height of the starting blob for the maximum vertical gap.
85 max_v_gap = height * v_gap_multiple;
86 if (ragged) {
87 // In the case of a ragged edge, we are much more generous with the
88 // inside alignment fraction, but also require a much bigger gutter.
91 l_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
92 r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
93 } else {
94 l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
95 r_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
96 }
98 } else {
100 l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
101 r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
103 }
104 min_gutter = static_cast<int>(height * gutter_fraction + 0.5);
105 if (min_gutter < min_gutter_width)
106 min_gutter = min_gutter_width;
107 // Fit the vertical vector into an ICOORD, which is 16 bit.
108 set_vertical(vertical_x, vertical_y);
109}
110
111// Constructor to set the parameters for finding vertical lines.
112// Vertical_x and vertical_y are the current estimates of the true vertical
113// direction (up) in the image. Width is the width of the starter blob.
114AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
115 int width)
116 : gutter_fraction(0.0),
117 right_tab(false),
118 ragged(false),
119 alignment(TA_SEPARATOR),
120 confirmed_type(TT_VLINE),
121 max_v_gap(kVLineSearchSize),
122 min_gutter(kVLineGutter),
123 min_points(1),
124 min_length(kVLineMinLength) {
125 // Compute threshold for left and right alignment.
126 l_align_tolerance = std::max(kVLineAlignment, width);
127 r_align_tolerance = std::max(kVLineAlignment, width);
128
129 // Fit the vertical vector into an ICOORD, which is 16 bit.
130 set_vertical(vertical_x, vertical_y);
131}
132
133// Fit the vertical vector into an ICOORD, which is 16 bit.
134void AlignedBlobParams::set_vertical(int vertical_x, int vertical_y) {
135 int factor = 1;
136 if (vertical_y > INT16_MAX)
137 factor = vertical_y / INT16_MAX + 1;
138 vertical.set_x(vertical_x / factor);
139 vertical.set_y(vertical_y / factor);
140}
141
142
144 const ICOORD& bleft, const ICOORD& tright)
145 : BlobGrid(gridsize, bleft, tright) {
146}
147
148// Return true if the given coordinates are within the test rectangle
149// and the debug level is at least the given detail level.
150bool AlignedBlob::WithinTestRegion(int detail_level, int x, int y) {
151 if (textord_debug_tabfind < detail_level)
152 return false;
153 return x >= textord_testregion_left && x <= textord_testregion_right &&
154 y <= textord_testregion_top && y >= textord_testregion_bottom;
155}
156
157// Display the tab codes of the BLOBNBOXes in this grid.
158ScrollView* AlignedBlob::DisplayTabs(const char* window_name,
159 ScrollView* tab_win) {
160#ifndef GRAPHICS_DISABLED
161 if (tab_win == nullptr)
162 tab_win = MakeWindow(0, 50, window_name);
163 // For every tab in the grid, display it.
165 gsearch.StartFullSearch();
166 BLOBNBOX* bbox;
167 while ((bbox = gsearch.NextFullSearch()) != nullptr) {
168 const TBOX& box = bbox->bounding_box();
169 int left_x = box.left();
170 int right_x = box.right();
171 int top_y = box.top();
172 int bottom_y = box.bottom();
173 TabType tabtype = bbox->left_tab_type();
174 if (tabtype != TT_NONE) {
175 if (tabtype == TT_MAYBE_ALIGNED)
176 tab_win->Pen(ScrollView::BLUE);
177 else if (tabtype == TT_MAYBE_RAGGED)
178 tab_win->Pen(ScrollView::YELLOW);
179 else if (tabtype == TT_CONFIRMED)
180 tab_win->Pen(ScrollView::GREEN);
181 else
182 tab_win->Pen(ScrollView::GREY);
183 tab_win->Line(left_x, top_y, left_x, bottom_y);
184 }
185 tabtype = bbox->right_tab_type();
186 if (tabtype != TT_NONE) {
187 if (tabtype == TT_MAYBE_ALIGNED)
188 tab_win->Pen(ScrollView::MAGENTA);
189 else if (tabtype == TT_MAYBE_RAGGED)
190 tab_win->Pen(ScrollView::ORANGE);
191 else if (tabtype == TT_CONFIRMED)
192 tab_win->Pen(ScrollView::RED);
193 else
194 tab_win->Pen(ScrollView::GREY);
195 tab_win->Line(right_x, top_y, right_x, bottom_y);
196 }
197 }
198 tab_win->Update();
199#endif
200 return tab_win;
201}
202
203// Helper returns true if the total number of line_crossings of all the blobs
204// in the list is at least 2.
205static bool AtLeast2LineCrossings(BLOBNBOX_CLIST* blobs) {
206 BLOBNBOX_C_IT it(blobs);
207 int total_crossings = 0;
208 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
209 total_crossings += it.data()->line_crossings();
210 }
211 return total_crossings >= 2;
212}
213
214// Destructor.
215// It is defined here, so the compiler can create a single vtable
216// instead of weak vtables in every compilation unit.
217AlignedBlob::~AlignedBlob() = default;
218
219// Finds a vector corresponding to a set of vertically aligned blob edges
220// running through the given box. The type of vector returned and the
221// search parameters are determined by the AlignedBlobParams.
222// vertical_x and y are updated with an estimate of the real
223// vertical direction. (skew finding.)
224// Returns nullptr if no decent vector can be found.
226 BLOBNBOX* bbox,
227 int* vertical_x,
228 int* vertical_y) {
229 int ext_start_y, ext_end_y;
230 BLOBNBOX_CLIST good_points;
231 // Search up and then down from the starting bbox.
232 TBOX box = bbox->bounding_box();
233 bool debug = WithinTestRegion(2, box.left(), box.bottom());
234 int pt_count = AlignTabs(align_params, false, bbox, &good_points, &ext_end_y);
235 pt_count += AlignTabs(align_params, true, bbox, &good_points, &ext_start_y);
236 BLOBNBOX_C_IT it(&good_points);
237 it.move_to_last();
238 box = it.data()->bounding_box();
239 int end_y = box.top();
240 int end_x = align_params.right_tab ? box.right() : box.left();
241 it.move_to_first();
242 box = it.data()->bounding_box();
243 int start_x = align_params.right_tab ? box.right() : box.left();
244 int start_y = box.bottom();
245 // Acceptable tab vectors must have a minimum number of points,
246 // have a minimum acceptable length, and have a minimum gradient.
247 // The gradient corresponds to the skew angle.
248 // Ragged tabs don't need to satisfy the gradient condition, as they
249 // will always end up parallel to the vertical direction.
250 bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
251 if ((pt_count >= align_params.min_points &&
252 end_y - start_y >= align_params.min_length &&
253 (align_params.ragged ||
254 end_y - start_y >= abs(end_x - start_x) * kMinTabGradient)) ||
255 at_least_2_crossings) {
256 int confirmed_points = 0;
257 // Count existing confirmed points to see if vector is acceptable.
258 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
259 bbox = it.data();
260 if (align_params.right_tab) {
261 if (bbox->right_tab_type() == align_params.confirmed_type)
262 ++confirmed_points;
263 } else {
264 if (bbox->left_tab_type() == align_params.confirmed_type)
265 ++confirmed_points;
266 }
267 }
268 // Ragged vectors are not allowed to use too many already used points.
269 if (!align_params.ragged ||
270 confirmed_points + confirmed_points < pt_count) {
271 const TBOX& box = bbox->bounding_box();
272 if (debug) {
273 tprintf("Confirming tab vector of %d pts starting at %d,%d\n",
274 pt_count, box.left(), box.bottom());
275 }
276 // Flag all the aligned neighbours as confirmed .
277 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
278 bbox = it.data();
279 if (align_params.right_tab) {
280 bbox->set_right_tab_type(align_params.confirmed_type);
281 } else {
282 bbox->set_left_tab_type(align_params.confirmed_type);
283 }
284 if (debug) {
285 bbox->bounding_box().print();
286 }
287 }
288 // Now make the vector and return it.
289 TabVector* result = TabVector::FitVector(align_params.alignment,
290 align_params.vertical,
291 ext_start_y, ext_end_y,
292 &good_points,
293 vertical_x, vertical_y);
294 result->set_intersects_other_lines(at_least_2_crossings);
295 if (debug) {
296 tprintf("Box was %d, %d\n", box.left(), box.bottom());
297 result->Print("After fitting");
298 }
299 return result;
300 } else if (debug) {
301 tprintf("Ragged tab used too many used points: %d out of %d\n",
302 confirmed_points, pt_count);
303 }
304 } else if (debug) {
305 tprintf("Tab vector failed basic tests: pt count %d vs min %d, "
306 "length %d vs min %d, min grad %g\n",
307 pt_count, align_params.min_points, end_y - start_y,
308 align_params.min_length, abs(end_x - start_x) * kMinTabGradient);
309 }
310 return nullptr;
311}
312
313// Find a set of blobs that are aligned in the given vertical
314// direction with the given blob. Returns a list of aligned
315// blobs and the number in the list.
316// For other parameters see FindAlignedBlob below.
317int AlignedBlob::AlignTabs(const AlignedBlobParams& params,
318 bool top_to_bottom, BLOBNBOX* bbox,
319 BLOBNBOX_CLIST* good_points, int* end_y) {
320 int ptcount = 0;
321 BLOBNBOX_C_IT it(good_points);
322
323 TBOX box = bbox->bounding_box();
324 bool debug = WithinTestRegion(2, box.left(), box.bottom());
325 if (debug) {
326 tprintf("Starting alignment run at blob:");
327 box.print();
328 }
329 int x_start = params.right_tab ? box.right() : box.left();
330 while (bbox != nullptr) {
331 // Add the blob to the list if the appropriate side is a tab candidate,
332 // or if we are working on a ragged tab.
333 TabType type = params.right_tab ? bbox->right_tab_type()
334 : bbox->left_tab_type();
335 if (((type != TT_NONE && type != TT_MAYBE_RAGGED) || params.ragged) &&
336 (it.empty() || it.data() != bbox)) {
337 if (top_to_bottom)
338 it.add_before_then_move(bbox);
339 else
340 it.add_after_then_move(bbox);
341 ++ptcount;
342 }
343 // Find the next blob that is aligned with the current one.
344 // FindAlignedBlob guarantees that forward progress will be made in the
345 // top_to_bottom direction, and therefore eventually it will return nullptr,
346 // making this while (bbox != nullptr) loop safe.
347 bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
348 if (bbox != nullptr) {
349 box = bbox->bounding_box();
350 if (!params.ragged)
351 x_start = params.right_tab ? box.right() : box.left();
352 }
353 }
354 if (debug) {
355 tprintf("Alignment run ended with %d pts at blob:", ptcount);
356 box.print();
357 }
358 return ptcount;
359}
360
361// Search vertically for a blob that is aligned with the input bbox.
362// The search parameters are determined by AlignedBlobParams.
363// top_to_bottom tells whether to search down or up.
364// The return value is nullptr if nothing was found in the search box
365// or if a blob was found in the gutter. On a nullptr return, end_y
366// is set to the edge of the search box or the leading edge of the
367// gutter blob if one was found.
368BLOBNBOX* AlignedBlob::FindAlignedBlob(const AlignedBlobParams& p,
369 bool top_to_bottom, BLOBNBOX* bbox,
370 int x_start, int* end_y) {
371 TBOX box = bbox->bounding_box();
372 // If there are separator lines, get the column edges.
373 int left_column_edge = bbox->left_rule();
374 int right_column_edge = bbox->right_rule();
375 // start_y is used to guarantee that forward progress is made and the
376 // search does not go into an infinite loop. New blobs must extend the
377 // line beyond start_y.
378 int start_y = top_to_bottom ? box.bottom() : box.top();
379 if (WithinTestRegion(2, x_start, start_y)) {
380 tprintf("Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
381 box.left(), box.top(), box.right(), box.bottom(),
382 left_column_edge, right_column_edge);
383 }
384 // Compute skew tolerance.
385 int skew_tolerance = p.max_v_gap / kMaxSkewFactor;
386 // Calculate xmin and xmax of the search box so that it contains
387 // all possibly relevant boxes up to p.max_v_gap above or below accoording
388 // to top_to_bottom.
389 // Start with a notion of vertical with the current estimate.
390 int x2 = (p.max_v_gap * p.vertical.x() + p.vertical.y()/2) / p.vertical.y();
391 if (top_to_bottom) {
392 x2 = x_start - x2;
393 *end_y = start_y - p.max_v_gap;
394 } else {
395 x2 = x_start + x2;
396 *end_y = start_y + p.max_v_gap;
397 }
398 // Expand the box by an additional skew tolerance
399 int xmin = std::min(x_start, x2) - skew_tolerance;
400 int xmax = std::max(x_start, x2) + skew_tolerance;
401 // Now add direction-specific tolerances.
402 if (p.right_tab) {
403 xmax += p.min_gutter;
404 xmin -= p.l_align_tolerance;
405 } else {
406 xmax += p.r_align_tolerance;
407 xmin -= p.min_gutter;
408 }
409 // Setup a vertical search for an aligned blob.
410 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(this);
411 if (WithinTestRegion(2, x_start, start_y))
412 tprintf("Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
413 p.ragged ? "Ragged" : "Aligned", p.right_tab ? "Right" : "Left",
414 xmin, xmax, start_y, p.max_v_gap, p.min_gutter);
415 vsearch.StartVerticalSearch(xmin, xmax, start_y);
416 // result stores the best real return value.
417 BLOBNBOX* result = nullptr;
418 // The backup_result is not a tab candidate and can be used if no
419 // real tab candidate result is found.
420 BLOBNBOX* backup_result = nullptr;
421 // neighbour is the blob that is currently being investigated.
422 BLOBNBOX* neighbour = nullptr;
423 while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) != nullptr) {
424 if (neighbour == bbox)
425 continue;
426 TBOX nbox = neighbour->bounding_box();
427 int n_y = (nbox.top() + nbox.bottom()) / 2;
428 if ((!top_to_bottom && n_y > start_y + p.max_v_gap) ||
429 (top_to_bottom && n_y < start_y - p.max_v_gap)) {
430 if (WithinTestRegion(2, x_start, start_y))
431 tprintf("Neighbour too far at (%d,%d)->(%d,%d)\n",
432 nbox.left(), nbox.bottom(), nbox.right(), nbox.top());
433 break; // Gone far enough.
434 }
435 // It is CRITICAL to ensure that forward progress is made, (strictly
436 // in/decreasing n_y) or the caller could loop infinitely, while
437 // waiting for a sequence of blobs in a line to end.
438 // NextVerticalSearch alone does not guarantee this, as there may be
439 // more than one blob in a grid cell. See comment in AlignTabs.
440 if ((n_y < start_y) != top_to_bottom || nbox.y_overlap(box))
441 continue; // Only look in the required direction.
442 if (result != nullptr && result->bounding_box().y_gap(nbox) > gridsize())
443 return result; // This result is clear.
444 if (backup_result != nullptr && p.ragged && result == nullptr &&
445 backup_result->bounding_box().y_gap(nbox) > gridsize())
446 return backup_result; // This result is clear.
447
448 // If the neighbouring blob is the wrong side of a separator line, then it
449 // "doesn't exist" as far as we are concerned.
450 int x_at_n_y = x_start + (n_y - start_y) * p.vertical.x() / p.vertical.y();
451 if (x_at_n_y < neighbour->left_crossing_rule() ||
452 x_at_n_y > neighbour->right_crossing_rule())
453 continue; // Separator line in the way.
454 int n_left = nbox.left();
455 int n_right = nbox.right();
456 int n_x = p.right_tab ? n_right : n_left;
457 if (WithinTestRegion(2, x_start, start_y))
458 tprintf("neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n",
459 nbox.left(), nbox.bottom(), nbox.right(), nbox.top(),
460 n_x, n_y, x_at_n_y);
461 if (p.right_tab &&
462 n_left < x_at_n_y + p.min_gutter &&
463 n_right > x_at_n_y + p.r_align_tolerance &&
464 (p.ragged || n_left < x_at_n_y + p.gutter_fraction * nbox.height())) {
465 // In the gutter so end of line.
466 if (bbox->right_tab_type() >= TT_MAYBE_ALIGNED)
468 *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
469 if (WithinTestRegion(2, x_start, start_y))
470 tprintf("gutter\n");
471 return nullptr;
472 }
473 if (!p.right_tab &&
474 n_left < x_at_n_y - p.l_align_tolerance &&
475 n_right > x_at_n_y - p.min_gutter &&
476 (p.ragged || n_right > x_at_n_y - p.gutter_fraction * nbox.height())) {
477 // In the gutter so end of line.
478 if (bbox->left_tab_type() >= TT_MAYBE_ALIGNED)
480 *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
481 if (WithinTestRegion(2, x_start, start_y))
482 tprintf("gutter\n");
483 return nullptr;
484 }
485 if ((p.right_tab && neighbour->leader_on_right()) ||
486 (!p.right_tab && neighbour->leader_on_left()))
487 continue; // Neighbours of leaders are not allowed to be used.
488 if (n_x <= x_at_n_y + p.r_align_tolerance &&
489 n_x >= x_at_n_y - p.l_align_tolerance) {
490 // Aligned so keep it. If it is a marked tab save it as result,
491 // otherwise keep it as backup_result to return in case of later failure.
492 if (WithinTestRegion(2, x_start, start_y))
493 tprintf("aligned, seeking%d, l=%d, r=%d\n",
494 p.right_tab, neighbour->left_tab_type(),
495 neighbour->right_tab_type());
496 TabType n_type = p.right_tab ? neighbour->right_tab_type()
497 : neighbour->left_tab_type();
498 if (n_type != TT_NONE && (p.ragged || n_type != TT_MAYBE_RAGGED)) {
499 if (result == nullptr) {
500 result = neighbour;
501 } else {
502 // Keep the closest neighbour by Euclidean distance.
503 // This prevents it from picking a tab blob in another column.
504 const TBOX& old_box = result->bounding_box();
505 int x_diff = p.right_tab ? old_box.right() : old_box.left();
506 x_diff -= x_at_n_y;
507 int y_diff = (old_box.top() + old_box.bottom()) / 2 - start_y;
508 int old_dist = x_diff * x_diff + y_diff * y_diff;
509 x_diff = n_x - x_at_n_y;
510 y_diff = n_y - start_y;
511 int new_dist = x_diff * x_diff + y_diff * y_diff;
512 if (new_dist < old_dist)
513 result = neighbour;
514 }
515 } else if (backup_result == nullptr) {
516 if (WithinTestRegion(2, x_start, start_y))
517 tprintf("Backup\n");
518 backup_result = neighbour;
519 } else {
520 TBOX backup_box = backup_result->bounding_box();
521 if ((p.right_tab && backup_box.right() < nbox.right()) ||
522 (!p.right_tab && backup_box.left() > nbox.left())) {
523 if (WithinTestRegion(2, x_start, start_y))
524 tprintf("Better backup\n");
525 backup_result = neighbour;
526 }
527 }
528 }
529 }
530 return result != nullptr ? result : backup_result;
531}
532
533} // namespace tesseract.
TabType
Definition: blobbox.h:59
@ TT_MAYBE_RAGGED
Definition: blobbox.h:62
@ TT_VLINE
Definition: blobbox.h:65
@ TT_DELETED
Definition: blobbox.h:61
@ TT_CONFIRMED
Definition: blobbox.h:64
@ TT_MAYBE_ALIGNED
Definition: blobbox.h:63
@ TT_NONE
Definition: blobbox.h:60
#define BOOL_VAR(name, val, comment)
Definition: params.h:306
#define INT_VAR(name, val, comment)
Definition: params.h:303
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:35
int textord_debug_bugs
Definition: alignedblob.cpp:28
bool textord_debug_printable
Definition: alignedblob.cpp:33
int textord_debug_tabfind
Definition: alignedblob.cpp:27
const int kVLineMinLength
Definition: alignedblob.cpp:56
const int kMinAlignedTabs
Definition: alignedblob.cpp:54
const int kVLineSearchSize
Definition: alignedblob.cpp:50
const int kVLineGutter
Definition: alignedblob.cpp:48
const double kAlignedGapFraction
Definition: alignedblob.cpp:42
const double kRaggedGapFraction
Definition: alignedblob.cpp:44
@ TA_RIGHT_ALIGNED
Definition: tabvector.h:48
@ TA_RIGHT_RAGGED
Definition: tabvector.h:49
@ TA_SEPARATOR
Definition: tabvector.h:50
@ TA_LEFT_RAGGED
Definition: tabvector.h:46
const int kVLineAlignment
Definition: alignedblob.cpp:46
const double kAlignedFraction
Definition: alignedblob.cpp:38
const int kMaxSkewFactor
Definition: alignedblob.cpp:64
const double kRaggedFraction
Definition: alignedblob.cpp:40
const int kMinRaggedTabs
Definition: alignedblob.cpp:52
const double kMinTabGradient
Definition: alignedblob.cpp:60
void set_left_tab_type(TabType new_type)
Definition: blobbox.h:274
bool leader_on_right() const
Definition: blobbox.h:364
void set_right_tab_type(TabType new_type)
Definition: blobbox.h:280
bool leader_on_left() const
Definition: blobbox.h:358
int right_crossing_rule() const
Definition: blobbox.h:331
TabType right_tab_type() const
Definition: blobbox.h:277
TabType left_tab_type() const
Definition: blobbox.h:271
int right_rule() const
Definition: blobbox.h:319
int left_rule() const
Definition: blobbox.h:313
const TBOX & bounding_box() const
Definition: blobbox.h:230
integer coordinate
Definition: points.h:32
void set_x(int16_t xin)
rewrite function
Definition: points.h:61
void set_y(int16_t yin)
rewrite function
Definition: points.h:65
Definition: rect.h:34
int16_t top() const
Definition: rect.h:58
void print() const
Definition: rect.h:278
int16_t height() const
Definition: rect.h:108
bool y_overlap(const TBOX &box) const
Definition: rect.h:428
int16_t left() const
Definition: rect.h:72
int y_gap(const TBOX &box) const
Definition: rect.h:233
int16_t bottom() const
Definition: rect.h:65
int16_t right() const
Definition: rect.h:79
void set_vertical(int vertical_x, int vertical_y)
AlignedBlobParams(int vertical_x, int vertical_y, int height, int v_gap_multiple, int min_gutter_width, int resolution, TabAlignment alignment0)
Definition: alignedblob.cpp:73
AlignedBlob(int gridsize, const ICOORD &bleft, const ICOORD &tright)
static bool WithinTestRegion(int detail_level, int x, int y)
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)
ScrollView * DisplayTabs(const char *window_name, ScrollView *tab_win)
void StartFullSearch()
Definition: bbgrid.h:665
BBC * NextFullSearch()
Definition: bbgrid.h:675
int gridsize() const
Definition: bbgrid.h:63
ScrollView * MakeWindow(int x, int y, const char *window_name)
Definition: bbgrid.h:589
void set_intersects_other_lines(bool value)
Definition: tabvector.h:181
static TabVector * FitVector(TabAlignment alignment, ICOORD vertical, int extended_start_y, int extended_end_y, BLOBNBOX_CLIST *good_points, int *vertical_x, int *vertical_y)
Definition: tabvector.cpp:176
void Print(const char *prefix)
Definition: tabvector.cpp:517
static void Update()
Definition: scrollview.cpp:709
void Line(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:532
void Pen(Color color)
Definition: scrollview.cpp:719