view mupdf-source/thirdparty/tesseract/src/textord/drawtord.cpp @ 38:8934ac156ef5

Allow to build with the PyPI package "clang" instead of "libclang". 1. It seems to be maintained. 2. In the FreeBSD base system there is no pre-built libclang.so. If you need this library you have to install llvm from ports additionally. 2. On FreeBSD there is no pre-built wheel "libclang" with a packaged libclang.so.
author Franz Glasner <fzglas.hg@dom66.de>
date Tue, 23 Sep 2025 10:27:15 +0200
parents b50eed0cc0ef
children
line wrap: on
line source

/**********************************************************************
 * File:        drawtord.cpp  (Formerly drawto.c)
 * Description: Draw things to do with textord.
 * Author:      Ray Smith
 *
 * (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.
 *
 **********************************************************************/

#ifdef HAVE_CONFIG_H
#  include "config_auto.h"
#endif

#include "drawtord.h"

#include "pithsync.h"
#include "topitch.h"

namespace tesseract {

#define TO_WIN_XPOS 0 // default window pos
#define TO_WIN_YPOS 0
#define TO_WIN_NAME "Textord"
// title of window

BOOL_VAR(textord_show_fixed_cuts, false, "Draw fixed pitch cell boundaries");

ScrollView *to_win = nullptr;

#ifndef GRAPHICS_DISABLED

/**********************************************************************
 * create_to_win
 *
 * Create the to window used to show the fit.
 **********************************************************************/

ScrollView *create_to_win(ICOORD page_tr) {
  if (to_win != nullptr) {
    return to_win;
  }
  to_win = new ScrollView(TO_WIN_NAME, TO_WIN_XPOS, TO_WIN_YPOS, page_tr.x() + 1, page_tr.y() + 1,
                          page_tr.x(), page_tr.y(), true);
  return to_win;
}

void close_to_win() {
  // to_win is leaked, but this enables the user to view the contents.
  if (to_win != nullptr) {
    to_win->Update();
  }
}

/**********************************************************************
 * plot_box_list
 *
 * Draw a list of blobs.
 **********************************************************************/

void plot_box_list(               // make gradients win
    ScrollView *win,              // window to draw in
    BLOBNBOX_LIST *list,          // blob list
    ScrollView::Color body_colour // colour to draw
) {
  BLOBNBOX_IT it = list; // iterator

  win->Pen(body_colour);
  win->Brush(ScrollView::NONE);
  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
    it.data()->bounding_box().plot(win);
  }
}

/**********************************************************************
 * plot_to_row
 *
 * Draw the blobs of a row in a given colour and draw the line fit.
 **********************************************************************/

void plot_to_row(             // draw a row
    TO_ROW *row,              // row to draw
    ScrollView::Color colour, // colour to draw in
    FCOORD rotation           // rotation for line
) {
  FCOORD plot_pt; // point to plot
                  // blobs
  BLOBNBOX_IT it = row->blob_list();
  float left, right; // end of row

  if (it.empty()) {
    tprintf("No blobs in row at %g\n", row->parallel_c());
    return;
  }
  left = it.data()->bounding_box().left();
  it.move_to_last();
  right = it.data()->bounding_box().right();
  plot_blob_list(to_win, row->blob_list(), colour, ScrollView::BROWN);
  to_win->Pen(colour);
  plot_pt = FCOORD(left, row->line_m() * left + row->line_c());
  plot_pt.rotate(rotation);
  to_win->SetCursor(plot_pt.x(), plot_pt.y());
  plot_pt = FCOORD(right, row->line_m() * right + row->line_c());
  plot_pt.rotate(rotation);
  to_win->DrawTo(plot_pt.x(), plot_pt.y());
}

/**********************************************************************
 * plot_parallel_row
 *
 * Draw the blobs of a row in a given colour and draw the line fit.
 **********************************************************************/

void plot_parallel_row(       // draw a row
    TO_ROW *row,              // row to draw
    float gradient,           // gradients of lines
    int32_t left,             // edge of block
    ScrollView::Color colour, // colour to draw in
    FCOORD rotation           // rotation for line
) {
  FCOORD plot_pt; // point to plot
                  // blobs
  BLOBNBOX_IT it = row->blob_list();
  auto fleft = static_cast<float>(left); // floating version
  float right;                           // end of row

  //      left=it.data()->bounding_box().left();
  it.move_to_last();
  right = it.data()->bounding_box().right();
  plot_blob_list(to_win, row->blob_list(), colour, ScrollView::BROWN);
  to_win->Pen(colour);
  plot_pt = FCOORD(fleft, gradient * left + row->max_y());
  plot_pt.rotate(rotation);
  to_win->SetCursor(plot_pt.x(), plot_pt.y());
  plot_pt = FCOORD(fleft, gradient * left + row->min_y());
  plot_pt.rotate(rotation);
  to_win->DrawTo(plot_pt.x(), plot_pt.y());
  plot_pt = FCOORD(fleft, gradient * left + row->parallel_c());
  plot_pt.rotate(rotation);
  to_win->SetCursor(plot_pt.x(), plot_pt.y());
  plot_pt = FCOORD(right, gradient * right + row->parallel_c());
  plot_pt.rotate(rotation);
  to_win->DrawTo(plot_pt.x(), plot_pt.y());
}

/**********************************************************************
 * draw_occupation
 *
 * Draw the row occupation with points above the threshold in white
 * and points below the threshold in black.
 **********************************************************************/

void draw_occupation(                    // draw projection
    int32_t xleft,                       // edge of block
    int32_t ybottom,                     // bottom of block
    int32_t min_y,                       // coordinate limits
    int32_t max_y, int32_t occupation[], // projection counts
    int32_t thresholds[]                 // for drop out
) {
  int32_t line_index;                     // pixel coord
  ScrollView::Color colour;               // of histogram
  auto fleft = static_cast<float>(xleft); // float version

  colour = ScrollView::WHITE;
  to_win->Pen(colour);
  to_win->SetCursor(fleft, static_cast<float>(ybottom));
  for (line_index = min_y; line_index <= max_y; line_index++) {
    if (occupation[line_index - min_y] < thresholds[line_index - min_y]) {
      if (colour != ScrollView::BLUE) {
        colour = ScrollView::BLUE;
        to_win->Pen(colour);
      }
    } else {
      if (colour != ScrollView::WHITE) {
        colour = ScrollView::WHITE;
        to_win->Pen(colour);
      }
    }
    to_win->DrawTo(fleft + occupation[line_index - min_y] / 10.0, static_cast<float>(line_index));
  }
  colour = ScrollView::STEEL_BLUE;
  to_win->Pen(colour);
  to_win->SetCursor(fleft, static_cast<float>(ybottom));
  for (line_index = min_y; line_index <= max_y; line_index++) {
    to_win->DrawTo(fleft + thresholds[line_index - min_y] / 10.0, static_cast<float>(line_index));
  }
}

/**********************************************************************
 * draw_meanlines
 *
 * Draw the meanlines of the given block in the given colour.
 **********************************************************************/

void draw_meanlines(          // draw a block
    TO_BLOCK *block,          // block to draw
    float gradient,           // gradients of lines
    int32_t left,             // edge of block
    ScrollView::Color colour, // colour to draw in
    FCOORD rotation           // rotation for line
) {
  FCOORD plot_pt; // point to plot
                  // rows
  TO_ROW_IT row_it = block->get_rows();
  TO_ROW *row;         // current row
  BLOBNBOX_IT blob_it; // blobs
  float right;         // end of row
  to_win->Pen(colour);
  for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) {
    row = row_it.data();
    blob_it.set_to_list(row->blob_list());
    blob_it.move_to_last();
    right = blob_it.data()->bounding_box().right();
    plot_pt = FCOORD(static_cast<float>(left), gradient * left + row->parallel_c() + row->xheight);
    plot_pt.rotate(rotation);
    to_win->SetCursor(plot_pt.x(), plot_pt.y());
    plot_pt = FCOORD(right, gradient * right + row->parallel_c() + row->xheight);
    plot_pt.rotate(rotation);
    to_win->DrawTo(plot_pt.x(), plot_pt.y());
  }
}

/**********************************************************************
 * plot_word_decisions
 *
 * Plot a row with words in different colours and fuzzy spaces
 * highlighted.
 **********************************************************************/

void plot_word_decisions( // draw words
    ScrollView *win,      // window tro draw in
    int16_t pitch,        // of block
    TO_ROW *row           // row to draw
) {
  ScrollView::Color colour = ScrollView::MAGENTA; // current colour
  ScrollView::Color rect_colour;                  // fuzzy colour
  int32_t prev_x;                                 // end of prev blob
  int16_t blob_count;                             // blobs in word
  BLOBNBOX *blob;                                 // current blob
  TBOX blob_box;                                  // bounding box
                                                  // iterator
  BLOBNBOX_IT blob_it = row->blob_list();
  BLOBNBOX_IT start_it = blob_it; // word start

  rect_colour = ScrollView::BLACK;
  prev_x = -INT16_MAX;
  blob_count = 0;
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) {
    blob = blob_it.data();
    blob_box = blob->bounding_box();
    if (!blob->joined_to_prev() && blob_box.left() - prev_x > row->max_nonspace) {
      if ((blob_box.left() - prev_x >= row->min_space ||
           blob_box.left() - prev_x > row->space_threshold) &&
          blob_count > 0) {
        if (pitch > 0 && textord_show_fixed_cuts) {
          plot_fp_cells(win, colour, &start_it, pitch, blob_count, &row->projection,
                        row->projection_left, row->projection_right,
                        row->xheight * textord_projection_scale);
        }
        blob_count = 0;
        start_it = blob_it;
      }
      if (colour == ScrollView::MAGENTA) {
        colour = ScrollView::RED;
      } else {
        colour = static_cast<ScrollView::Color>(colour + 1);
      }
      if (blob_box.left() - prev_x < row->min_space) {
        if (blob_box.left() - prev_x > row->space_threshold) {
          rect_colour = ScrollView::GOLDENROD;
        } else {
          rect_colour = ScrollView::CORAL;
        }
        // fill_color_index(win, rect_colour);
        win->Brush(rect_colour);
        win->Rectangle(prev_x, blob_box.bottom(), blob_box.left(), blob_box.top());
      }
    }
    if (!blob->joined_to_prev()) {
      prev_x = blob_box.right();
    }
    if (blob->cblob() != nullptr) {
      blob->cblob()->plot(win, colour, colour);
    }
    if (!blob->joined_to_prev() && blob->cblob() != nullptr) {
      blob_count++;
    }
  }
  if (pitch > 0 && textord_show_fixed_cuts && blob_count > 0) {
    plot_fp_cells(win, colour, &start_it, pitch, blob_count, &row->projection, row->projection_left,
                  row->projection_right, row->xheight * textord_projection_scale);
  }
}

/**********************************************************************
 * plot_fp_cells
 *
 * Make a list of fixed pitch cuts and draw them.
 **********************************************************************/

void plot_fp_cells(           // draw words
    ScrollView *win,          // window tro draw in
    ScrollView::Color colour, // colour of lines
    BLOBNBOX_IT *blob_it,     // blobs
    int16_t pitch,            // of block
    int16_t blob_count,       // no of real blobs
    STATS *projection,        // vertical
    int16_t projection_left,  // edges //scale factor
    int16_t projection_right, float projection_scale) {
  int16_t occupation;    // occupied cells
  TBOX word_box;         // bounding box
  FPSEGPT_LIST seg_list; // list of cuts
  FPSEGPT_IT seg_it;
  FPSEGPT *segpt; // current point

  if (pitsync_linear_version) {
    check_pitch_sync2(blob_it, blob_count, pitch, 2, projection, projection_left, projection_right,
                      projection_scale, occupation, &seg_list, 0, 0);
  } else {
    check_pitch_sync(blob_it, blob_count, pitch, 2, projection, &seg_list);
  }
  word_box = blob_it->data()->bounding_box();
  for (; blob_count > 0; blob_count--) {
    word_box += box_next(blob_it);
  }
  seg_it.set_to_list(&seg_list);
  for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) {
    segpt = seg_it.data();
    if (segpt->faked) {
      colour = ScrollView::WHITE;
      win->Pen(colour);
    } else {
      win->Pen(colour);
    }
    win->Line(segpt->position(), word_box.bottom(), segpt->position(), word_box.top());
  }
}

/**********************************************************************
 * plot_fp_cells2
 *
 * Make a list of fixed pitch cuts and draw them.
 **********************************************************************/

void plot_fp_cells2(          // draw words
    ScrollView *win,          // window tro draw in
    ScrollView::Color colour, // colour of lines
    TO_ROW *row,              // for location
    FPSEGPT_LIST *seg_list    // segments to plot
) {
  TBOX word_box; // bounding box
  FPSEGPT_IT seg_it = seg_list;
  // blobs in row
  BLOBNBOX_IT blob_it = row->blob_list();
  FPSEGPT *segpt; // current point

  word_box = blob_it.data()->bounding_box();
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list();) {
    word_box += box_next(&blob_it);
  }
  for (seg_it.mark_cycle_pt(); !seg_it.cycled_list(); seg_it.forward()) {
    segpt = seg_it.data();
    if (segpt->faked) {
      colour = ScrollView::WHITE;
      win->Pen(colour);
    } else {
      win->Pen(colour);
    }
    win->Line(segpt->position(), word_box.bottom(), segpt->position(), word_box.top());
  }
}

/**********************************************************************
 * plot_row_cells
 *
 * Make a list of fixed pitch cuts and draw them.
 **********************************************************************/

void plot_row_cells(          // draw words
    ScrollView *win,          // window tro draw in
    ScrollView::Color colour, // colour of lines
    TO_ROW *row,              // for location
    float xshift,             // amount of shift
    ICOORDELT_LIST *cells     // cells to draw
) {
  TBOX word_box; // bounding box
  ICOORDELT_IT cell_it = cells;
  // blobs in row
  BLOBNBOX_IT blob_it = row->blob_list();
  ICOORDELT *cell; // current cell

  word_box = blob_it.data()->bounding_box();
  for (blob_it.mark_cycle_pt(); !blob_it.cycled_list();) {
    word_box += box_next(&blob_it);
  }
  win->Pen(colour);
  for (cell_it.mark_cycle_pt(); !cell_it.cycled_list(); cell_it.forward()) {
    cell = cell_it.data();
    win->Line(cell->x() + xshift, word_box.bottom(), cell->x() + xshift, word_box.top());
  }
}

#endif // !GRAPHICS_DISABLED

} // namespace tesseract