|
|
// Copyright 2005-2024 Google LLC
//
// 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.
//
// 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 <iomanip>
#include <ios>
#include <ostream>
#include <sstream>
#include <string>
#include <fst/log.h>
#include <fst/fst.h>
#include <fst/properties.h>
#include <fst/symbol-table.h>
#include <fst/util.h>
#include <fst/script/fst-class.h>
#include <string_view>
namespace fst {
// Print a binary FST in GraphViz textual format (helper class for fstdraw.cc).
// WARNING: Stand-alone use not recommend.
template <class Arc> class FstDrawer { public: using Label = typename Arc::Label; using StateId = typename Arc::StateId; using Weight = typename Arc::Weight;
FstDrawer(const Fst<Arc> &fst, const SymbolTable *isyms, const SymbolTable *osyms, const SymbolTable *ssyms, bool accep, std::string_view title, float width, float height, bool portrait, bool vertical, float ranksep, float nodesep, int fontsize, int precision, std::string_view float_format, bool show_weight_one) : fst_(fst), isyms_(isyms), osyms_(osyms), ssyms_(ssyms), accep_(accep && fst.Properties(kAcceptor, true)), 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, std::string_view dest) { SetStreamState(strm); dest_ = std::string(dest); const auto start = fst_.Start(); if (start == kNoStateId) return; strm << "digraph FST {\n"; if (vertical_) { strm << "rankdir = BT;\n"; } else { strm << "rankdir = LR;\n"; } strm << "size = \"" << width_ << "," << height_ << "\";\n"; if (!title_.empty()) strm << "label = \"" + title_ + "\";\n"; strm << "center = 1;\n"; if (portrait_) { strm << "orientation = Portrait;\n"; } else { strm << "orientation = Landscape;\n"; } strm << "ranksep = \"" << ranksep_ << "\";\n" << "nodesep = \"" << nodesep_ << "\";\n"; // Initial state first.
DrawState(strm, start); for (StateIterator<Fst<Arc>> siter(fst_); !siter.Done(); siter.Next()) { const auto s = siter.Value(); if (s != start) DrawState(strm, s); } strm << "}\n"; }
private: void SetStreamState(std::ostream &strm) const { strm << std::setprecision(precision_); if (float_format_ == "e") strm << std::scientific; if (float_format_ == "f") strm << std::fixed; // O.w. defaults to "g" per standard lib.
}
// Escapes backslash and double quote if these occur in the string. Dot
// will not deal gracefully with these if they are not escaped.
static std::string Escape(std::string_view str) { std::string ns; for (char c : str) { if (c == '\\' || c == '"') ns.push_back('\\'); ns.push_back(c); } return ns; }
std::string FormatId(StateId id, const SymbolTable *syms) 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 = "?"; } return Escape(symbol); } else { return std::to_string(id); } }
std::string FormatStateId(StateId s) const { return FormatId(s, ssyms_); }
std::string FormatILabel(Label label) const { return FormatId(label, isyms_); }
std::string FormatOLabel(Label label) const { return FormatId(label, osyms_); }
std::string FormatWeight(Weight w) const { std::stringstream ss; SetStreamState(ss); ss << w; // Weight may have double quote characters in it, so escape it.
return Escape(ss.str()); }
void DrawState(std::ostream &strm, StateId s) const { strm << s << " [label = \"" << FormatStateId(s); const auto weight = fst_.Final(s); if (weight != Weight::Zero()) { if (show_weight_one_ || (weight != Weight::One())) { strm << "/" << FormatWeight(weight); } strm << "\", shape = doublecircle,"; } else { strm << "\", shape = circle,"; } if (s == fst_.Start()) { strm << " style = bold,"; } else { strm << " style = solid,"; } strm << " fontsize = " << fontsize_ << "]\n"; for (ArcIterator<Fst<Arc>> aiter(fst_, s); !aiter.Done(); aiter.Next()) { const auto &arc = aiter.Value(); strm << "\t" << s << " -> " << arc.nextstate << " [label = \"" << FormatILabel(arc.ilabel); if (!accep_) { strm << ":" << FormatOLabel(arc.olabel); } if (show_weight_one_ || (arc.weight != Weight::One())) { strm << "/" << FormatWeight(arc.weight); } strm << "\", fontsize = " << fontsize_ << "];\n"; } }
const Fst<Arc> &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::string dest_; // Drawn FST destination name.
std::string title_; float width_; float height_; bool portrait_; bool vertical_; float ranksep_; float nodesep_; int fontsize_; int precision_; std::string float_format_; bool show_weight_one_;
FstDrawer(const FstDrawer &) = delete; FstDrawer &operator=(const FstDrawer &) = delete; };
} // namespace fst
#endif // FST_SCRIPT_DRAW_IMPL_H_
|