You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

212 lines
6.4 KiB

// 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_