Doxygen
Loading...
Searching...
No Matches
plantuml.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include "plantuml.h"
17#include "util.h"
18#include "portable.h"
19#include "config.h"
20#include "doxygen.h"
21#include "message.h"
22#include "debug.h"
23#include "fileinfo.h"
24#include "dir.h"
25#include "indexlist.h"
26#include "stringutil.h"
27
29 const QCString &content,OutputFormat format, const QCString &engine,
30 const QCString &srcFile,int srcLine,bool inlineCode)
31{
32 QCString baseName;
33 QCString puName;
34 QCString imgName;
35 QCString outDir(outDirArg);
36 static int umlindex=1;
37
38 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource fileName: {}\n",fileName);
39 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource outDir: {}\n",outDir);
40
41 // strip any trailing slashes and backslashes
42 size_t l = 0;
43 while ((l=outDir.length())>0 && (outDir.at(l-1)=='/' || outDir.at(l-1)=='\\'))
44 {
45 outDir = outDir.left(l-1);
46 }
47
48 if (fileName.isEmpty()) // generate name
49 {
50 puName = "inline_umlgraph_"+QCString().setNum(umlindex);
51 baseName = outDir+"/inline_umlgraph_"+QCString().setNum(umlindex++);
52 }
53 else // user specified name
54 {
55 baseName = fileName;
56 int i=baseName.findRev('.');
57 if (i!=-1) baseName = baseName.left(i);
58 puName = baseName;
59 baseName.prepend(outDir+"/");
60 }
61
62 switch (format)
63 {
64 case PUML_BITMAP:
65 imgName =puName+".png";
66 break;
67 case PUML_EPS:
68 imgName =puName+".eps";
69 break;
70 case PUML_SVG:
71 imgName =puName+".svg";
72 break;
73 }
74
75 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName: {}\n",baseName);
76 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName puName: {}\n",puName);
77 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName imgName: {}\n",imgName);
78
79 QCString text;
80 if (inlineCode) text = "@start"+engine+" "+imgName+"\n";
81 text.reserve(text.length()+content.length()+100); // add room for image name and end marker
82 const char *p = content.data();
83 if (p)
84 {
85 char c = 0;
86 bool insideComment = false;
87 bool initial = true;
88 while ((c=*p++))
89 {
90 text+=c;
91 switch (c)
92 {
93 case '\'': insideComment=true; break;
94 case '\n': insideComment=false; break;
95 case '\t': break;
96 case ' ': break;
97 case '@':
98 if (initial && literal_at(p,"start")) // @start...
99 {
100 while ((c=*p++) && isId(c)) text+=c;
101 // insert the image name
102 text+=' ';
103 text+=imgName;
104 if (c) text+=c;
105 }
106 break;
107 default:
108 if (!insideComment) initial=false;
109 break;
110 }
111 }
112 text+='\n';
113 }
114 if (inlineCode) text +="@end"+engine+"\n";
115
116 //printf("content\n====\n%s\n=====\n->\n-----\n%s\n------\n",qPrint(content),qPrint(text));
117
118 QCString qcOutDir(substitute(outDir,"\\","/"));
119 uint32_t pos = qcOutDir.findRev("/");
120 QCString generateType(qcOutDir.right(qcOutDir.length() - (pos + 1)) );
121 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
122 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
123 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
124
125 return baseName;
126}
127
128void PlantumlManager::generatePlantUMLOutput(const QCString &baseName,const QCString &/* outDir */,OutputFormat format)
129{
130 QCString imgName = baseName;
131 // The basename contains path, we need to strip the path from the filename in order
132 // to create the image file name which should be included in the index.qhp (Qt help index file).
133 int i = imgName.findRev('/');
134 if (i!=-1) // strip path
135 {
136 imgName=imgName.mid(i+1);
137 }
138 switch (format)
139 {
140 case PUML_BITMAP:
141 imgName+=".png";
142 break;
143 case PUML_EPS:
144 imgName+=".eps";
145 break;
146 case PUML_SVG:
147 imgName+=".svg";
148 break;
149 }
150
151 Doxygen::indexList->addImageFile(imgName);
152}
153
154//--------------------------------------------------------------------
155
156
158{
159 static PlantumlManager theInstance;
160 return theInstance;
161}
162
166
167static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles,
168 const PlantumlManager::ContentMap &plantumlContent,
170{
171 /* example : running: java -Djava.awt.headless=true
172 -jar "/usr/local/bin/plantuml.jar"
173 -o "test_doxygen/DOXYGEN_OUTPUT/html"
174 -tpng
175 "test_doxygen/DOXYGEN_OUTPUT/html/A.pu"
176 -charset UTF-8
177 outDir:test_doxygen/DOXYGEN_OUTPUT/html
178 test_doxygen/DOXYGEN_OUTPUT/html/A
179 */
180 int exitCode = 0;
181 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
182 QCString plantumlConfigFile = Config_getString(PLANTUML_CFG_FILE);
183
184 QCString pumlExe = "java";
185 QCString pumlArgs = "";
186 QCString pumlType = "";
187 QCString pumlOutDir = "";
188
189 const StringVector &pumlIncludePathList = Config_getList(PLANTUML_INCLUDE_PATH);
190 {
191 auto it = pumlIncludePathList.begin();
192 if (it!=pumlIncludePathList.end())
193 {
194 pumlArgs += "-Dplantuml.include.path=\"";
195 pumlArgs += it->c_str();
196 ++it;
197 }
198 while (it!=pumlIncludePathList.end())
199 {
200 pumlArgs += Portable::pathListSeparator();
201 pumlArgs += it->c_str();
202 ++it;
203 }
204 }
205 if (!pumlIncludePathList.empty()) pumlArgs += "\" ";
206 pumlArgs += "-Djava.awt.headless=true -jar \""+plantumlJarPath+"\" ";
207 if (!plantumlConfigFile.isEmpty())
208 {
209 pumlArgs += "-config \"";
210 pumlArgs += plantumlConfigFile;
211 pumlArgs += "\" ";
212 }
213 // the -graphvizdot option expects a relative or absolute path to the dot executable, so
214 // we need to use the unverified DOT_PATH option and check if it points to an existing file.
215 QCString dotPath = Config_getString(DOT_PATH);
216 FileInfo dp(dotPath.str());
217 if (Config_getBool(HAVE_DOT) && dp.exists() && dp.isFile())
218 {
219 pumlArgs += "-graphvizdot \"";
220 pumlArgs += dotPath;
221 pumlArgs += "\" ";
222 }
223 switch (format)
224 {
226 pumlType="png";
227 break;
229 pumlType="eps";
230 break;
232 pumlType="svg";
233 break;
234 }
235
236 {
237 for (const auto &[name,nb] : plantumlContent)
238 {
239 if (nb.content.isEmpty()) continue;
240
241 QCString pumlArguments = pumlArgs;
242 msg("Generating PlantUML {} Files in {}\n",pumlType,name);
243 pumlArguments+="-o \"";
244 pumlArguments+=nb.outDir;
245 pumlArguments+="\" ";
246 pumlArguments+="-charset UTF-8 -t";
247 pumlArguments+=pumlType;
248 pumlArguments+=" ";
249
250 QCString puFileName("");
251 puFileName+=nb.outDir;
252 puFileName+="/";
253 pumlOutDir=puFileName;
254 puFileName+="inline_umlgraph_";
255 puFileName+=pumlType;
256 puFileName+=name.c_str();
257 puFileName+=".pu";
258
259 pumlArguments+="\"";
260 pumlArguments+=puFileName;
261 pumlArguments+="\" ";
262
263
264 QCString cachedContent;
265 FileInfo fi(puFileName.str());
266 if (fi.exists())
267 {
268 cachedContent = fileToString(puFileName);
269 }
270
271 std::ofstream file = Portable::openOutputStream(puFileName);
272 if (!file.is_open())
273 {
274 err_full(nb.srcFile,nb.srcLine,"Could not open file {} for writing",puFileName);
275 }
276 file.write( nb.content.data(), nb.content.length() );
277 file.close();
278 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running Plantuml arguments:{}\n",pumlArguments);
279
280 if (cachedContent == nb.content) continue;
281
282 if ((exitCode=Portable::system(pumlExe.data(),pumlArguments.data(),TRUE))!=0)
283 {
284 err_full(nb.srcFile,nb.srcLine,"Problems running PlantUML. Verify that the command 'java -jar \"{}\" -h' works from the command line. Exit code: {}.",
285 plantumlJarPath,exitCode);
286 }
287
288 if ( (format==PlantumlManager::PUML_EPS) && (Config_getBool(USE_PDFLATEX)) )
289 {
290 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running epstopdf\n");
291 auto files_kv = plantumlFiles.find(name);
292 if (files_kv!=plantumlFiles.end())
293 {
294 for (const auto &str : files_kv->second)
295 {
296 const int maxCmdLine = 40960;
298 epstopdfArgs.sprintf("\"%s%s.eps\" --outfile=\"%s%s.pdf\"",
299 pumlOutDir.data(),str.c_str(), pumlOutDir.data(),str.c_str());
300 if ((exitCode=Portable::system("epstopdf",epstopdfArgs.data()))!=0)
301 {
302 err_full(nb.srcFile,nb.srcLine,"Problems running epstopdf. Check your TeX installation! Exit code: {}.",exitCode);
303 }
304 else
305 {
306 Dir().remove(pumlOutDir.str()+str+".eps");
307 }
308 }
309 }
310 }
311 }
312 }
313}
314
322
323static void print(const PlantumlManager::FilesMap &plantumlFiles)
324{
326 {
327 for (const auto &[key,list] : plantumlFiles)
328 {
329 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Files PlantumlFiles key:{} size:{}\n",key,list.size());
330 for (const auto &s : list)
331 {
332 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print list:{}\n",s);
333 }
334 }
335 }
336}
337
338static void print(const PlantumlManager::ContentMap &plantumlContent)
339{
341 {
342 for (const auto &[key,content] : plantumlContent)
343 {
344 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content PlantumlContent key: {}\n",key);
345 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content:\n{}\n",content.content);
346 }
347 }
348}
349
351 const std::string &key, const std::string &value)
352{
353 auto kv = plantumlFiles.find(key);
354 if (kv==plantumlFiles.end())
355 {
356 kv = plantumlFiles.emplace(key,StringVector()).first;
357 }
358 kv->second.push_back(value);
359}
360
362 const std::string &key, const QCString &outDir, const QCString &puContent,
363 const QCString &srcFile,int srcLine)
364{
365 auto kv = plantumlContent.find(key);
366 if (kv==plantumlContent.end())
367 {
368 kv = plantumlContent.emplace(key,PlantumlContent("",outDir,srcFile,srcLine)).first;
369 }
370 kv->second.content+=puContent;
371}
372
373void PlantumlManager::insert(const std::string &key, const std::string &value,
374 const QCString &outDir,OutputFormat format,const QCString &puContent,
375 const QCString &srcFile,int srcLine)
376{
377 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::insert key:{} ,value:{}\n",key,value);
378
379 switch (format)
380 {
381 case PUML_BITMAP:
384 addPlantumlContent(m_pngPlantumlContent,key,outDir,puContent,srcFile,srcLine);
386 break;
387 case PUML_EPS:
390 addPlantumlContent(m_epsPlantumlContent,key,outDir,puContent,srcFile,srcLine);
392 break;
393 case PUML_SVG:
396 addPlantumlContent(m_svgPlantumlContent,key,outDir,puContent,srcFile,srcLine);
398 break;
399 }
400}
401
402//--------------------------------------------------------------------
@ Plantuml
Definition debug.h:39
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:76
Class representing a directory in the file system.
Definition dir.h:75
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
static IndexList * indexList
Definition doxygen.h:134
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
bool isFile() const
Definition fileinfo.cpp:63
ContentMap m_pngPlantumlContent
Definition plantuml.h:90
std::map< std::string, PlantumlContent > ContentMap
Definition plantuml.h:76
QCString writePlantUMLSource(const QCString &outDirArg, const QCString &fileName, const QCString &content, OutputFormat format, const QCString &engine, const QCString &srcFile, int srcLine, bool inlineCode)
Write a PlantUML compatible file.
Definition plantuml.cpp:28
OutputFormat
Plant UML output image formats.
Definition plantuml.h:44
ContentMap m_epsPlantumlContent
Definition plantuml.h:92
std::map< std::string, StringVector > FilesMap
Definition plantuml.h:75
void insert(const std::string &key, const std::string &value, const QCString &outDir, OutputFormat format, const QCString &puContent, const QCString &srcFile, int srcLine)
Definition plantuml.cpp:373
ContentMap m_svgPlantumlContent
Definition plantuml.h:91
FilesMap m_pngPlantumlFiles
Definition plantuml.h:87
static PlantumlManager & instance()
Definition plantuml.cpp:157
FilesMap m_svgPlantumlFiles
Definition plantuml.h:88
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:315
void generatePlantUMLOutput(const QCString &baseName, const QCString &outDir, OutputFormat format)
Convert a PlantUML file to an image.
Definition plantuml.cpp:128
FilesMap m_epsPlantumlFiles
Definition plantuml.h:89
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString & prepend(const char *s)
Definition qcstring.h:407
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:578
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
const std::string & str() const
Definition qcstring.h:537
QCString & setNum(short n)
Definition qcstring.h:444
QCString right(size_t len) const
Definition qcstring.h:219
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:172
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:133
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:91
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
QCString left(size_t len) const
Definition qcstring.h:214
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::vector< std::string > StringVector
Definition containers.h:33
static const int maxCmdLine
Definition dia.cpp:24
#define msg(fmt,...)
Definition message.h:94
#define err_full(file, line, fmt,...)
Definition message.h:132
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:665
QCString pathListSeparator()
Definition portable.cpp:400
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:106
static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles, const PlantumlManager::ContentMap &plantumlContent, PlantumlManager::OutputFormat format)
Definition plantuml.cpp:167
static void addPlantumlContent(PlantumlManager::ContentMap &plantumlContent, const std::string &key, const QCString &outDir, const QCString &puContent, const QCString &srcFile, int srcLine)
Definition plantuml.cpp:361
static void print(const PlantumlManager::FilesMap &plantumlFiles)
Definition plantuml.cpp:323
static void addPlantumlFiles(PlantumlManager::FilesMap &plantumlFiles, const std::string &key, const std::string &value)
Definition plantuml.cpp:350
Portable versions of functions that are platform dependent.
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:477
#define TRUE
Definition qcstring.h:37
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns TRUE iff data points to a substring that matches string literal str
Definition stringutil.h:98
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1441
A bunch of utility functions.
bool isId(int c)
Definition util.h:208