/**********************************************************************
 * File:        pango_font_info.h
 * Description: Font-related objects and helper functions
 * Author:      Ranjith Unnikrishnan
 * Created:     Mon Nov 18 2013
 *
 * (C) Copyright 2013, Google Inc.
 * 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.
 *
 **********************************************************************/

#ifndef TESSERACT_TRAINING_PANGO_FONT_INFO_H_
#define TESSERACT_TRAINING_PANGO_FONT_INFO_H_

#include "export.h"

#include "commandlineflags.h"

#include "pango/pango-font.h"
#include "pango/pango.h"
#include "pango/pangocairo.h"

#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using char32 = signed int;

namespace tesseract {

// Data holder class for a font, intended to avoid having to work with Pango or
// FontConfig-specific objects directly.
class TESS_PANGO_TRAINING_API PangoFontInfo {
public:
  enum FontTypeEnum {
    UNKNOWN,
    SERIF,
    SANS_SERIF,
    DECORATIVE,
  };
  PangoFontInfo();
  ~PangoFontInfo();
  // Initialize from parsing a font description name, defined as a string of the
  // format:
  //   "FamilyName [FaceName] [PointSize]"
  // where a missing FaceName implies the default regular face.
  // eg. "Arial Italic 12", "Verdana"
  //
  // FaceName is a combination of:
  //   [StyleName] [Variant] [Weight] [Stretch]
  // with (all optional) Pango-defined values of:
  // StyleName: Oblique, Italic
  // Variant  : Small-Caps
  // Weight   : Ultra-Light, Light, Medium, Semi-Bold, Bold, Ultra-Bold, Heavy
  // Stretch  : Ultra-Condensed, Extra-Condensed, Condensed, Semi-Condensed,
  //            Semi-Expanded, Expanded, Extra-Expanded, Ultra-Expanded.
  explicit PangoFontInfo(const std::string &name);
  bool ParseFontDescriptionName(const std::string &name);

  // Returns true if the font have codepoint coverage for the specified text.
  bool CoversUTF8Text(const char *utf8_text, int byte_length) const;
  // Modifies string to remove unicode points that are not covered by the
  // font. Returns the number of characters dropped.
  int DropUncoveredChars(std::string *utf8_text) const;

  // Returns true if the entire string can be rendered by the font with full
  // character coverage and no unknown glyph or dotted-circle glyph
  // substitutions on encountering a badly formed unicode sequence.
  // If true, returns individual graphemes. Any whitespace characters in the
  // original string are also included in the list.
  bool CanRenderString(const char *utf8_word, int len, std::vector<std::string> *graphemes) const;
  bool CanRenderString(const char *utf8_word, int len) const;

  // Retrieves the x_bearing and x_advance for the given utf8 character in the
  // font. Returns false if the glyph for the character could not be found in
  // the font.
  // Ref: http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-3.html
  bool GetSpacingProperties(const std::string &utf8_char, int *x_bearing, int *x_advance) const;

  // If not already initialized, initializes FontConfig by setting its
  // environment variable and creating a fonts.conf file that points to the
  // FLAGS_fonts_dir and the cache to FLAGS_fontconfig_tmpdir.
  static void SoftInitFontConfig();
  // Re-initializes font config, whether or not already initialized.
  // If already initialized, any existing cache is deleted, just to be sure.
  static void HardInitFontConfig(const char *fonts_dir, const char *cache_dir);

  // Accessors
  std::string DescriptionName() const;
  // Font Family name eg. "Arial"
  const std::string &family_name() const {
    return family_name_;
  }
  // Size in points (1/72"), rounded to the nearest integer.
  int font_size() const {
    return font_size_;
  }
  FontTypeEnum font_type() const {
    return font_type_;
  }

  int resolution() const {
    return resolution_;
  }
  void set_resolution(const int resolution) {
    resolution_ = resolution;
  }

private:
  friend class FontUtils;
  void Clear();
  bool ParseFontDescription(const PangoFontDescription *desc);
  // Returns the PangoFont structure corresponding to the closest available font
  // in the font map.
  PangoFont *ToPangoFont() const;

  // Font properties set automatically from parsing the font description name.
  std::string family_name_;
  int font_size_;
  FontTypeEnum font_type_;
  // The Pango description that was used to initialize the instance.
  PangoFontDescription *desc_;
  // Default output resolution to assume for GetSpacingProperties() and any
  // other methods that returns pixel values.
  int resolution_;
  // Fontconfig operates through an environment variable, so it intrinsically
  // cannot be thread-friendly, but you can serialize multiple independent
  // font configurations by calling HardInitFontConfig(fonts_dir, cache_dir).
  // These hold the last initialized values set by HardInitFontConfig or
  // the first call to SoftInitFontConfig.
  // Directory to be scanned for font files.
  static std::string fonts_dir_;
  // Directory to store the cache of font information. (Can be the same as
  // fonts_dir_)
  static std::string cache_dir_;

private:
  PangoFontInfo(const PangoFontInfo &) = delete;
  void operator=(const PangoFontInfo &) = delete;
};

// Static utility methods for querying font availability and font-selection
// based on codepoint coverage.
class TESS_PANGO_TRAINING_API FontUtils {
public:
  // Returns true if the font of the given description name is available in the
  // target directory specified by --fonts_dir
  static bool IsAvailableFont(const char *font_desc) {
    return IsAvailableFont(font_desc, nullptr);
  }
  // Returns true if the font of the given description name is available in the
  // target directory specified by --fonts_dir. If false is returned, and
  // best_match is not nullptr, the closest matching font is returned there.
  static bool IsAvailableFont(const char *font_desc, std::string *best_match);
  // Outputs description names of available fonts.
  static const std::vector<std::string> &ListAvailableFonts();

  // Picks font among available fonts that covers and can render the given word,
  // and returns the font description name and the decomposition of the word to
  // graphemes. Returns false if no suitable font was found.
  static bool SelectFont(const char *utf8_word, const int utf8_len, std::string *font_name,
                         std::vector<std::string> *graphemes);

  // Picks font among all_fonts that covers and can render the given word,
  // and returns the font description name and the decomposition of the word to
  // graphemes. Returns false if no suitable font was found.
  static bool SelectFont(const char *utf8_word, const int utf8_len,
                         const std::vector<std::string> &all_fonts, std::string *font_name,
                         std::vector<std::string> *graphemes);

  // NOTE: The following utilities were written to be backward compatible with
  // StringRender.

  // BestFonts returns a font name and a bit vector of the characters it
  // can render for the fonts that score within some fraction of the best
  // font on the characters in the given hash map.
  // In the flags vector, each flag is set according to whether the
  // corresponding character (in order of iterating ch_map) can be rendered.
  // The return string is a list of the acceptable fonts that were used.
  static std::string BestFonts(const std::unordered_map<char32, int64_t> &ch_map,
                               std::vector<std::pair<const char *, std::vector<bool>>> *font_flag);

  // FontScore returns the weighted renderability score of the given
  // hash map character table in the given font. The unweighted score
  // is also returned in raw_score.
  // The values in the bool vector ch_flags correspond to whether the
  // corresponding character (in order of iterating ch_map) can be rendered.
  static int FontScore(const std::unordered_map<char32, int64_t> &ch_map,
                       const std::string &fontname, int *raw_score, std::vector<bool> *ch_flags);

  // PangoFontInfo is reinitialized, so clear the static list of fonts.
  static void ReInit();
  static void PangoFontTypeInfo();

private:
  static std::vector<std::string> available_fonts_; // cache list
};
} // namespace tesseract

#endif // TESSERACT_TRAINING_PANGO_FONT_INFO_H_
