1// draw.h
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Copyright 2005-2010 Google, Inc.
16// Author: allauzen@google.com (Cyril Allauzen)
17//
18// \file
19// Class to draw a binary FST by producing a text file in dot format,
20// helper class to fstdraw.cc
21
22#ifndef FST_SCRIPT_DRAW_IMPL_H_
23#define FST_SCRIPT_DRAW_IMPL_H_
24
25#include <sstream>
26#include <string>
27
28#include <fst/script/fst-class.h>
29#include <fst/fst.h>
30#include <fst/util.h>
31
32namespace fst {
33
34// Print a binary Fst in the dot textual format, helper class for fstdraw.cc
35// WARNING: Stand-alone use not recommend.
36template <class A> class FstDrawer {
37 public:
38  typedef A Arc;
39  typedef typename A::StateId StateId;
40  typedef typename A::Label Label;
41  typedef typename A::Weight Weight;
42
43  FstDrawer(const Fst<A> &fst,
44            const SymbolTable *isyms,
45            const SymbolTable *osyms,
46            const SymbolTable *ssyms,
47            bool accep,
48            string title,
49            float width,
50            float height,
51            bool portrait,
52            bool vertical,
53            float ranksep,
54            float nodesep,
55            int fontsize,
56            int precision,
57            bool show_weight_one)
58      : fst_(fst), isyms_(isyms), osyms_(osyms), ssyms_(ssyms),
59        accep_(accep && fst.Properties(kAcceptor, true)), ostrm_(0),
60        title_(title), width_(width), height_(height), portrait_(portrait),
61        vertical_(vertical), ranksep_(ranksep), nodesep_(nodesep),
62        fontsize_(fontsize), precision_(precision),
63        show_weight_one_(show_weight_one) {}
64
65  // Draw Fst to an output buffer (or stdout if buf = 0)
66  void Draw(ostream *strm, const string &dest) {
67    ostrm_ = strm;
68    dest_ = dest;
69    StateId start = fst_.Start();
70    if (start == kNoStateId)
71      return;
72
73    PrintString("digraph FST {\n");
74    if (vertical_)
75      PrintString("rankdir = BT;\n");
76    else
77      PrintString("rankdir = LR;\n");
78    PrintString("size = \"");
79    Print(width_);
80    PrintString(",");
81    Print(height_);
82    PrintString("\";\n");
83    if (!dest_.empty())
84      PrintString("label = \"" + title_ + "\";\n");
85    PrintString("center = 1;\n");
86    if (portrait_)
87      PrintString("orientation = Portrait;\n");
88    else
89      PrintString("orientation = Landscape;\n");
90    PrintString("ranksep = \"");
91    Print(ranksep_);
92    PrintString("\";\n");
93    PrintString("nodesep = \"");
94    Print(nodesep_);
95    PrintString("\";\n");
96    // initial state first
97    DrawState(start);
98    for (StateIterator< Fst<A> > siter(fst_);
99         !siter.Done();
100         siter.Next()) {
101      StateId s = siter.Value();
102      if (s != start)
103        DrawState(s);
104    }
105    PrintString("}\n");
106  }
107
108 private:
109  // Maximum line length in text file.
110  static const int kLineLen = 8096;
111
112  void PrintString(const string &s) const {
113    *ostrm_ << s;
114  }
115
116  // Escapes backslash and double quote if these occur in the string. Dot will
117  // not deal gracefully with these if they are not escaped.
118  inline void EscapeChars(const string &s, string* ns) const {
119    const char* c = s.c_str();
120    while (*c) {
121      if (*c == '\\' || *c == '"') ns->push_back('\\');
122      ns->push_back(*c);
123      ++c;
124    }
125  }
126
127  void PrintId(int64 id, const SymbolTable *syms,
128               const char *name) const {
129    if (syms) {
130      string symbol = syms->Find(id);
131      if (symbol == "") {
132        FSTERROR() << "FstDrawer: Integer " << id
133                   << " is not mapped to any textual symbol"
134                   << ", symbol table = " << syms->Name()
135                   << ", destination = " << dest_;
136        symbol = "?";
137      }
138      string nsymbol;
139      EscapeChars(symbol, &nsymbol);
140      PrintString(nsymbol);
141    } else {
142      string idstr;
143      Int64ToStr(id, &idstr);
144      PrintString(idstr);
145    }
146  }
147
148  void PrintStateId(StateId s) const {
149     PrintId(s, ssyms_, "state ID");
150  }
151
152  void PrintILabel(Label l) const {
153     PrintId(l, isyms_, "arc input label");
154  }
155
156  void PrintOLabel(Label l) const {
157     PrintId(l, osyms_, "arc output label");
158  }
159
160  template <class T>
161  void Print(T t) const {
162    *ostrm_ << t;
163  }
164
165  void DrawState(StateId s) const {
166    Print(s);
167    PrintString(" [label = \"");
168    PrintStateId(s);
169    Weight final = fst_.Final(s);
170    if (final != Weight::Zero()) {
171      if (show_weight_one_ || (final != Weight::One())) {
172        PrintString("/");
173        Print(final);
174      }
175      PrintString("\", shape = doublecircle,");
176    } else {
177      PrintString("\", shape = circle,");
178    }
179    if (s == fst_.Start())
180      PrintString(" style = bold,");
181    else
182      PrintString(" style = solid,");
183    PrintString(" fontsize = ");
184    Print(fontsize_);
185    PrintString("]\n");
186    for (ArcIterator< Fst<A> > aiter(fst_, s);
187         !aiter.Done();
188         aiter.Next()) {
189      Arc arc = aiter.Value();
190      PrintString("\t");
191      Print(s);
192      PrintString(" -> ");
193      Print(arc.nextstate);
194      PrintString(" [label = \"");
195      PrintILabel(arc.ilabel);
196      if (!accep_) {
197        PrintString(":");
198        PrintOLabel(arc.olabel);
199      }
200      if (show_weight_one_ || (arc.weight != Weight::One())) {
201        PrintString("/");
202        Print(arc.weight);
203      }
204      PrintString("\", fontsize = ");
205      Print(fontsize_);
206      PrintString("];\n");
207    }
208  }
209
210  const Fst<A> &fst_;
211  const SymbolTable *isyms_;     // ilabel symbol table
212  const SymbolTable *osyms_;     // olabel symbol table
213  const SymbolTable *ssyms_;     // slabel symbol table
214  bool accep_;                   // print as acceptor when possible
215  ostream *ostrm_;               // drawn FST destination
216  string dest_;                  // drawn FST destination name
217
218  string title_;
219  float width_;
220  float height_;
221  bool portrait_;
222  bool vertical_;
223  float ranksep_;
224  float nodesep_;
225  int fontsize_;
226  int precision_;
227  bool show_weight_one_;
228
229  DISALLOW_COPY_AND_ASSIGN(FstDrawer);
230};
231
232}  // namespace fst
233
234#endif  // FST_SCRIPT_DRAW_IMPL_H_
235