40 #include "allheaders.h"      53 #ifdef USE_STD_NAMESPACE    70                 "Degrade rendered image with speckle noise, dilation/erosion "    74 BOOL_PARAM_FLAG(rotate_image, 
true, 
"Rotate the image in a random way.");
   100                   "Fraction of words to underline (value in [0,1])");
   104                   "Fraction of words to underline (value in [0,1])");
   111                   "Specify one of the following writing"   113                   "'horizontal' : Render regular horizontal text. (default)\n"   114                   "'vertical' : Render vertical text. Glyph orientation is"   115                   " selected by Pango.\n"   116                   "'vertical-upright' : Render vertical text. Glyph "   117                   " orientation is set to be upright.");
   119 INT_PARAM_FLAG(box_padding, 0, 
"Padding around produced bounding boxes");
   122                 "Remove unrenderable words from source text");
   128                 "Rebuild and render ligatures");
   131                 "Search for all fonts that can render the text");
   133                 "If find_fonts==true, render each font to its own image. "   134                 "Image filenames are of the form output_name.font_name.tif");
   136                   "If find_fonts==true, the minimum coverage the font has of "   137                   "the characters in the text file to include it, between "   140 BOOL_PARAM_FLAG(list_available_fonts, 
false, 
"List available fonts and quit.");
   142 BOOL_PARAM_FLAG(render_ngrams, 
false, 
"Put each space-separated entity from the"   143                 " input file into one bounding box. The ngrams in the input"   144                 " file will be randomly permuted before rendering (so that"   145                 " there is sufficient variety of characters on each line).");
   148                 "Output word bounding boxes instead of character boxes. "   149                 "This is used for Cube training, and implied by "   153                   "File with characters in the unicharset. If --render_ngrams"   154                   " is true and --unicharset_file is specified, ngrams with"   155                   " characters that are not in unicharset will be omitted");
   158                 "Rotate the generated characters both ways.");
   161                 "Assumes that the input file contains a list of ngrams. Renders"   162                 " each ngram, extracts spacing properties and records them in"   163                 " output_base/[font_name].fontinfo file.");
   167                 "If true also outputs individual character images");
   169                "Each glyph is square with this side length in pixels");
   171                "Final_size=glyph_resized_size+2*glyph_num_border_pixels_to_pad");
   185 static bool IsWhitespaceBox(
const BoxChar* boxchar) {
   186   return (boxchar->
box() == 
nullptr ||
   190 static string StringReplace(
const string& in,
   191                             const string& oldsub, 
const string& newsub) {
   193   size_t start_pos = 0, pos;
   194   while ((pos = in.find(oldsub, start_pos)) != string::npos) {
   195     out.append(in.data() + start_pos, pos - start_pos);
   196     out.append(newsub.data(), newsub.length());
   197     start_pos = pos + oldsub.length();
   199   out.append(in.data() + start_pos, in.length() - start_pos);
   215                            const string &output_base) {
   216   std::map<string, SpacingProperties> spacing_map;
   217   std::map<string, SpacingProperties>::iterator spacing_map_it0;
   218   std::map<string, SpacingProperties>::iterator spacing_map_it1;
   219   int x_bearing, x_advance;
   220   int len = utf8_text.length();
   222   const char* text = utf8_text.c_str();
   223   while (offset < len) {
   225         render->
RenderToImage(text + offset, strlen(text + offset), 
nullptr);
   226     const std::vector<BoxChar*> &boxes = render->
GetBoxes();
   230     if (boxes.size() > 2 && !IsWhitespaceBox(boxes[boxes.size() - 1]) &&
   231         IsWhitespaceBox(boxes[boxes.size() - 2])) {
   232       if (boxes.size() > 3) {
   233         tprintf(
"WARNING: Adjusting to bad page break after '%s%s'\n",
   234                 boxes[boxes.size() - 4]->ch().c_str(),
   235                 boxes[boxes.size() - 3]->ch().c_str());
   237       offset -= boxes[boxes.size() - 1]->ch().size();
   240     for (
size_t b = 0; b < boxes.size(); b += 2) {
   241       while (b < boxes.size() && IsWhitespaceBox(boxes[b])) ++b;
   242       if (b + 1 >= boxes.size()) 
break;
   243       const string &ch0 = boxes[b]->ch();
   256       if (IsWhitespaceBox(boxes[b+1])) {
   259       int xgap = (boxes[b+1]->box()->x -
   260                   (boxes[b]->box()->x + boxes[b]->box()->w));
   261       spacing_map_it0 = spacing_map.find(ch0);
   263       if (spacing_map_it0 == spacing_map.end() &&
   266             x_bearing, x_advance - x_bearing - boxes[b]->box()->w);
   267         spacing_map_it0 = spacing_map.find(ch0);
   270       const string &ch1 = boxes[b+1]->ch();
   271       tlog(3, 
"%s%s\n", ch0.c_str(), ch1.c_str());
   272       spacing_map_it1 = spacing_map.find(ch1);
   273       if (spacing_map_it1 == spacing_map.end() &&
   276             x_bearing, x_advance - x_bearing - boxes[b+1]->box()->w);
   277         spacing_map_it1 = spacing_map.find(ch1);
   280       if (ok_count == 2 && xgap != (spacing_map_it0->second.x_gap_after +
   281                                     spacing_map_it1->second.x_gap_before)) {
   282         spacing_map_it0->second.kerned_x_gaps[ch1] = xgap;
   287   string output_string;
   288   const int kBufSize = 1024;
   290   snprintf(buf, kBufSize, 
"%d\n", static_cast<int>(spacing_map.size()));
   291   output_string.append(buf);
   292   std::map<string, SpacingProperties>::const_iterator spacing_map_it;
   293   for (spacing_map_it = spacing_map.begin();
   294        spacing_map_it != spacing_map.end(); ++spacing_map_it) {
   295     snprintf(buf, kBufSize,
   296              "%s %d %d %d", spacing_map_it->first.c_str(),
   297              spacing_map_it->second.x_gap_before,
   298              spacing_map_it->second.x_gap_after,
   299              static_cast<int>(spacing_map_it->second.kerned_x_gaps.size()));
   300     output_string.append(buf);
   301     std::map<string, int>::const_iterator kern_it;
   302     for (kern_it = spacing_map_it->second.kerned_x_gaps.begin();
   303          kern_it != spacing_map_it->second.kerned_x_gaps.end(); ++kern_it) {
   304       snprintf(buf, kBufSize,
   305                " %s %d", kern_it->first.c_str(), kern_it->second);
   306       output_string.append(buf);
   308     output_string.append(
"\n");
   314                           const std::vector<BoxChar*>& vbox,
   315                           const int input_tiff_page) {
   318     tprintf(
"ERROR: MakeIndividualGlyphs(): Input Pix* is nullptr\n");
   320   } 
else if (FLAGS_glyph_resized_size <= 0) {
   321     tprintf(
"ERROR: --glyph_resized_size must be positive\n");
   323   } 
else if (FLAGS_glyph_num_border_pixels_to_pad < 0) {
   324     tprintf(
"ERROR: --glyph_num_border_pixels_to_pad must be 0 or positive\n");
   328   const int n_boxes = vbox.size();
   329   int n_boxes_saved = 0;
   330   int current_tiff_page = 0;
   332   static int glyph_count = 0;
   333   for (
int i = 0; i < n_boxes; i++) {
   335     Box* b = vbox[i]->mutable_box();
   342     if (y < y_previous-pixGetHeight(pix)/10) {
   343       tprintf(
"ERROR: Wrap-around encountered, at i=%d\n", i);
   346     if (current_tiff_page < input_tiff_page) 
continue;
   347     else if (current_tiff_page > input_tiff_page) 
break;
   349     if (x < 0 || y < 0 ||
   350         (x+w-1) >= pixGetWidth(pix) ||
   351         (y+h-1) >= pixGetHeight(pix)) {
   352       tprintf(
"ERROR: MakeIndividualGlyphs(): Index out of range, at i=%d"   353               " (x=%d, y=%d, w=%d, h=%d\n)", i, x, y, w, h);
   355     } 
else if (w < FLAGS_glyph_num_border_pixels_to_pad &&
   356                h < FLAGS_glyph_num_border_pixels_to_pad) {
   357       tprintf(
"ERROR: Input image too small to be a character, at i=%d\n", i);
   361     Pix* pix_glyph = pixClipRectangle(pix, b, 
nullptr);
   363       tprintf(
"ERROR: MakeIndividualGlyphs(): Failed to clip, at i=%d\n", i);
   367     Pix* pix_glyph_sq = pixScaleToSize(pix_glyph,
   368                                        FLAGS_glyph_resized_size,
   369                                        FLAGS_glyph_resized_size);
   371       tprintf(
"ERROR: MakeIndividualGlyphs(): Failed to resize, at i=%d\n", i);
   375     Pix* pix_glyph_sq_pad = pixAddBorder(pix_glyph_sq,
   376                                          FLAGS_glyph_num_border_pixels_to_pad,
   378     if (!pix_glyph_sq_pad) {
   379       tprintf(
"ERROR: MakeIndividualGlyphs(): Failed to zero-pad, at i=%d\n",
   384     Pix* pix_glyph_sq_pad_8 = pixConvertTo8(pix_glyph_sq_pad, 
false);
   386     snprintf(filename, 1024, 
"%s_%d.jpg", FLAGS_outputbase.c_str(),
   388     if (pixWriteJpeg(filename, pix_glyph_sq_pad_8, 100, 0)) {
   389       tprintf(
"ERROR: MakeIndividualGlyphs(): Failed to write JPEG to %s,"   390               " at i=%d\n", filename, i);
   394     pixDestroy(&pix_glyph);
   395     pixDestroy(&pix_glyph_sq);
   396     pixDestroy(&pix_glyph_sq_pad);
   397     pixDestroy(&pix_glyph_sq_pad_8);
   401   if (n_boxes_saved == 0) {
   404     tprintf(
"Total number of characters saved = %d\n", n_boxes_saved);
   418 int main(
int argc, 
char** argv) {
   421   if (FLAGS_list_available_fonts) {
   423     for (
unsigned int i = 0; i < all_fonts.size(); ++i) {
   424       printf(
"%3u: %s\n", i, all_fonts[i].c_str());
   426                       "Font %s is unrecognized.\n", all_fonts[i].c_str());
   432   if (FLAGS_text.empty()) {
   433     tprintf(
"'--text' option is missing!\n");
   436   if (FLAGS_outputbase.empty()) {
   437     tprintf(
"'--outputbase' option is missing!\n");
   440   if (!FLAGS_unicharset_file.empty() && FLAGS_render_ngrams) {
   441     tprintf(
"Use '--unicharset_file' only if '--render_ngrams' is set.\n");
   448       tprintf(
"Could not find font named %s.\n", FLAGS_font.c_str());
   449       if (!pango_name.empty()) {
   450         tprintf(
"Pango suggested font %s.\n", pango_name.c_str());
   452       tprintf(
"Please correct --font arg.\n");
   457   if (FLAGS_render_ngrams)
   458     FLAGS_output_word_boxes = 
true;
   460   char font_desc_name[1024];
   461   snprintf(font_desc_name, 1024, 
"%s %d", FLAGS_font.c_str(),
   462            static_cast<int>(FLAGS_ptsize));
   477   if (FLAGS_writing_mode == 
"horizontal") {
   482   } 
else if (FLAGS_writing_mode == 
"vertical") {
   487   } 
else if (FLAGS_writing_mode == 
"vertical-upright") {
   497     tprintf(
"Invalid writing mode: %s\n", FLAGS_writing_mode.c_str());
   504     tprintf(
"Failed to read file: %s\n", FLAGS_text.c_str());
   509   if (strncmp(src_utf8.c_str(), 
"\xef\xbb\xbf", 3) == 0) {
   510     src_utf8.erase(0, 3);
   512   tlog(1, 
"Render string of size %d\n", src_utf8.length());
   514   if (FLAGS_render_ngrams || FLAGS_only_extract_font_properties) {
   517     const string kSeparator = FLAGS_render_ngrams ? 
"    " : 
" ";
   521     const unsigned int kCharsPerLine = (FLAGS_ptsize > 20) ? 50 : 100;
   524     if (FLAGS_render_ngrams && !FLAGS_unicharset_file.empty() &&
   526       tprintf(
"Failed to load unicharset from file %s\n",
   527               FLAGS_unicharset_file.c_str());
   534     const char *str8 = src_utf8.c_str();
   535     int len = src_utf8.length();
   537     std::vector<std::pair<int, int> > offsets;
   539     while (offset < len) {
   541       offsets.push_back(std::make_pair(offset, step));
   545     if (FLAGS_render_ngrams)
   546       std::random_shuffle(offsets.begin(), offsets.end());
   548     for (
size_t i = 0, line = 1; i < offsets.size(); ++i) {
   549       const char *curr_pos = str8 + offsets[i].first;
   550       int ngram_len = offsets[i].second;
   552       if (!FLAGS_unicharset_file.empty() &&
   556       rand_utf8.append(curr_pos, ngram_len);
   557       if (rand_utf8.length() > line * kCharsPerLine) {
   558         rand_utf8.append(
" \n");
   560         if (line & 0x1) rand_utf8.append(kSeparator);
   562         rand_utf8.append(kSeparator);
   565     tlog(1, 
"Rendered ngram string of size %d\n", rand_utf8.length());
   566     src_utf8.swap(rand_utf8);
   568   if (FLAGS_only_extract_font_properties) {
   569     tprintf(
"Extracting font properties only\n");
   576   std::vector<float> page_rotation;
   577   const char* to_render_utf8 = src_utf8.c_str();
   581   std::vector<string> font_names;
   585   int num_pass = FLAGS_bidirectional_rotation ? 2 : 1;
   586   for (
int pass = 0; pass < num_pass; ++pass) {
   589     for (
size_t offset = 0; 
offset < strlen(to_render_utf8); ++im, ++page_num) {
   590       tlog(1, 
"Starting page %d\n", im);
   592       if (FLAGS_find_fonts) {
   595                                                strlen(to_render_utf8 + 
offset),
   599                                        strlen(to_render_utf8 + 
offset), &pix);
   601       if (pix != 
nullptr) {
   605           rotation = -1 * page_rotation[page_num];
   607         if (FLAGS_degrade_image) {
   609                              FLAGS_rotate_image ? &rotation : 
nullptr);
   615           page_rotation.push_back(rotation);
   618         Pix* gray_pix = pixConvertTo8(pix, 
false);
   620         Pix* binary = pixThresholdToBinary(gray_pix, 128);
   621         pixDestroy(&gray_pix);
   622         char tiff_name[1024];
   623         if (FLAGS_find_fonts) {
   624           if (FLAGS_render_per_font) {
   625             string fontname_for_file = tesseract::StringReplace(
   626                 font_used, 
" ", 
"_");
   627             snprintf(tiff_name, 1024, 
"%s.%s.tif", FLAGS_outputbase.c_str(),
   628                      fontname_for_file.c_str());
   629             pixWriteTiff(tiff_name, binary, IFF_TIFF_G4, 
"w");
   630             tprintf(
"Rendered page %d to file %s\n", im, tiff_name);
   632             font_names.push_back(font_used);
   635           snprintf(tiff_name, 1024, 
"%s.tif", FLAGS_outputbase.c_str());
   636           pixWriteTiff(tiff_name, binary, IFF_TIFF_G4, im == 0 ? 
"w" : 
"a");
   637           tprintf(
"Rendered page %d to file %s\n", im, tiff_name);
   640         if (FLAGS_output_individual_glyph_images) {
   642             tprintf(
"ERROR: Individual glyphs not saved\n");
   647       if (FLAGS_find_fonts && 
offset != 0) {
   654   if (!FLAGS_find_fonts) {
   655     string box_name = FLAGS_outputbase.c_str();
   658   } 
else if (!FLAGS_render_per_font && !font_names.empty()) {
   659     string filename = FLAGS_outputbase.c_str();
   660     filename += 
".fontlist.txt";
   661     FILE* fp = fopen(filename.c_str(), 
"wb");
   663       tprintf(
"Failed to create output font list %s\n", filename.c_str());
   665       for (
size_t i = 0; i < font_names.size(); ++i) {
   666         fprintf(fp, 
"%s\n", font_names[i].c_str());
 int SpanUTF8NotWhitespace(const char *text)
static void WriteStringToFileOrDie(const string &str, const string &filename)
void WriteAllBoxes(const string &filename)
INT_PARAM_FLAG(exposure, 0, "Exposure level in photocopier")
SpacingProperties(int b, int a)
#define ASSERT_HOST_MSG(x,...)
void set_box_padding(int val)
void ExtractFontProperties(const string &utf8_text, StringRenderer *render, const string &output_base)
DOUBLE_PARAM_FLAG(char_spacing, 0, "Inter-character space in ems")
static const std::vector< string > & ListAvailableFonts()
bool MakeIndividualGlyphs(Pix *pix, const std::vector< BoxChar *> &vbox, const int input_tiff_page)
void set_add_ligatures(bool add_ligatures)
const string & ch() const
void set_strip_unrenderable_words(bool val)
int RenderToImage(const char *text, int text_length, Pix **pix)
bool encodable_string(const char *str, int *first_bad_position) const
int RenderAllFontsToImage(double min_coverage, const char *text, int text_length, string *font_used, Pix **pix)
bool GetSpacingProperties(const string &utf8_char, int *x_bearing, int *x_advance) const
void set_gravity_hint_strong(bool gravity_hint_strong)
void set_underline_continuation_prob(const double frac)
void set_underline_start_prob(const double frac)
static bool IsAvailableFont(const char *font_desc)
void set_char_spacing(double char_spacing)
bool load_from_file(const char *const filename, bool skip_fragments)
STRING_PARAM_FLAG(text, "", "File name of text input to process")
Pix * DegradeImage(Pix *input, int exposure, TRand *randomizer, float *rotation)
void set_resolution(const int resolution)
void set_leading(int leading)
void set_render_fullwidth_latin(bool render_fullwidth_latin)
void set_seed(uinT64 seed)
int main(int argc, char **argv)
void set_vertical_text(bool vertical_text)
void set_v_margin(const int v_margin)
const std::vector< BoxChar * > & GetBoxes() const
BOOL_PARAM_FLAG(degrade_image, true, "Degrade rendered image with speckle noise, dilation/erosion " "and rotation")
void RotatePageBoxes(float rotation)
int SpanUTF8Whitespace(const char *text)
void set_output_word_boxes(bool val)
void ParseCommandLineFlags(const char *usage, int *argc, char ***argv, const bool remove_flags)
std::map< string, int > kerned_x_gaps
void set_h_margin(const int h_margin)
const PangoFontInfo & font() const
static bool ReadFileToString(const string &filename, string *out)