// See www.openfst.org for extensive documentation on this weighted // finite-state transducer library. // // Class to draw a binary FST by producing a text file in dot format, a helper // class to fstdraw.cc. #ifndef FST_SCRIPT_DRAW_IMPL_H_ #define FST_SCRIPT_DRAW_IMPL_H_ #include #include #include #include #include #include namespace fst { // Print a binary FST in GraphViz textual format (helper class for fstdraw.cc). // WARNING: Stand-alone use not recommend. template class FstDrawer { public: using Label = typename Arc::Label; using StateId = typename Arc::StateId; using Weight = typename Arc::Weight; FstDrawer(const Fst &fst, const SymbolTable *isyms, const SymbolTable *osyms, const SymbolTable *ssyms, bool accep, const string &title, float width, float height, bool portrait, bool vertical, float ranksep, float nodesep, int fontsize, int precision, const string &float_format, bool show_weight_one) : fst_(fst), isyms_(isyms), osyms_(osyms), ssyms_(ssyms), accep_(accep && fst.Properties(kAcceptor, true)), ostrm_(nullptr), title_(title), width_(width), height_(height), portrait_(portrait), vertical_(vertical), ranksep_(ranksep), nodesep_(nodesep), fontsize_(fontsize), precision_(precision), float_format_(float_format), show_weight_one_(show_weight_one) {} // Draws FST to an output buffer. void Draw(std::ostream *strm, const string &dest) { ostrm_ = strm; SetStreamState(ostrm_); dest_ = dest; StateId start = fst_.Start(); if (start == kNoStateId) return; PrintString("digraph FST {\n"); if (vertical_) { PrintString("rankdir = BT;\n"); } else { PrintString("rankdir = LR;\n"); } PrintString("size = \""); Print(width_); PrintString(","); Print(height_); PrintString("\";\n"); if (!dest_.empty()) PrintString("label = \"" + title_ + "\";\n"); PrintString("center = 1;\n"); if (portrait_) { PrintString("orientation = Portrait;\n"); } else { PrintString("orientation = Landscape;\n"); } PrintString("ranksep = \""); Print(ranksep_); PrintString("\";\n"); PrintString("nodesep = \""); Print(nodesep_); PrintString("\";\n"); // Initial state first. DrawState(start); for (StateIterator> siter(fst_); !siter.Done(); siter.Next()) { const auto s = siter.Value(); if (s != start) DrawState(s); } PrintString("}\n"); } private: void SetStreamState(std::ostream* strm) const { strm->precision(precision_); if (float_format_ == "e") strm->setf(std::ios_base::scientific, std::ios_base::floatfield); if (float_format_ == "f") strm->setf(std::ios_base::fixed, std::ios_base::floatfield); // O.w. defaults to "g" per standard lib. } void PrintString(const string &str) const { *ostrm_ << str; } // Escapes backslash and double quote if these occur in the string. Dot will // not deal gracefully with these if they are not escaped. static string Escape(const string &str) { string ns; for (char c : str) { if (c == '\\' || c == '"') ns.push_back('\\'); ns.push_back(c); } return ns; } void PrintId(StateId id, const SymbolTable *syms, const char *name) const { if (syms) { auto symbol = syms->Find(id); if (symbol.empty()) { FSTERROR() << "FstDrawer: Integer " << id << " is not mapped to any textual symbol" << ", symbol table = " << syms->Name() << ", destination = " << dest_; symbol = "?"; } PrintString(Escape(symbol)); } else { PrintString(std::to_string(id)); } } void PrintStateId(StateId s) const { PrintId(s, ssyms_, "state ID"); } void PrintILabel(Label label) const { PrintId(label, isyms_, "arc input label"); } void PrintOLabel(Label label) const { PrintId(label, osyms_, "arc output label"); } void PrintWeight(Weight w) const { // Weight may have double quote characters in it, so escape it. PrintString(Escape(ToString(w))); } template void Print(T t) const { *ostrm_ << t; } template string ToString(T t) const { std::stringstream ss; SetStreamState(&ss); ss << t; return ss.str(); } void DrawState(StateId s) const { Print(s); PrintString(" [label = \""); PrintStateId(s); const auto weight = fst_.Final(s); if (weight != Weight::Zero()) { if (show_weight_one_ || (weight != Weight::One())) { PrintString("/"); PrintWeight(weight); } PrintString("\", shape = doublecircle,"); } else { PrintString("\", shape = circle,"); } if (s == fst_.Start()) { PrintString(" style = bold,"); } else { PrintString(" style = solid,"); } PrintString(" fontsize = "); Print(fontsize_); PrintString("]\n"); for (ArcIterator> aiter(fst_, s); !aiter.Done(); aiter.Next()) { const auto &arc = aiter.Value(); PrintString("\t"); Print(s); PrintString(" -> "); Print(arc.nextstate); PrintString(" [label = \""); PrintILabel(arc.ilabel); if (!accep_) { PrintString(":"); PrintOLabel(arc.olabel); } if (show_weight_one_ || (arc.weight != Weight::One())) { PrintString("/"); PrintWeight(arc.weight); } PrintString("\", fontsize = "); Print(fontsize_); PrintString("];\n"); } } const Fst &fst_; const SymbolTable *isyms_; // ilabel symbol table. const SymbolTable *osyms_; // olabel symbol table. const SymbolTable *ssyms_; // slabel symbol table. bool accep_; // Print as acceptor when possible. std::ostream *ostrm_; // Drawn FST destination. string dest_; // Drawn FST destination name. string title_; float width_; float height_; bool portrait_; bool vertical_; float ranksep_; float nodesep_; int fontsize_; int precision_; string float_format_; bool show_weight_one_; FstDrawer(const FstDrawer &) = delete; FstDrawer &operator=(const FstDrawer &) = delete; }; } // namespace fst #endif // FST_SCRIPT_DRAW_IMPL_H_