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
28 const QCString &content,OutputFormat format, const QCString &engine,
29 const QCString &srcFile,int srcLine,bool inlineCode)
30{
31 QCString baseName;
32 QCString puName;
33 QCString imgName;
34 QCString outDir(outDirArg);
35 static int umlindex=1;
36
37 Debug::print(Debug::Plantuml,0,"*** %s fileName: %s\n","writePlantUMLSource",qPrint(fileName));
38 Debug::print(Debug::Plantuml,0,"*** %s outDir: %s\n","writePlantUMLSource",qPrint(outDir));
39
40 // strip any trailing slashes and backslashes
41 size_t l = 0;
42 while ((l=outDir.length())>0 && (outDir.at(l-1)=='/' || outDir.at(l-1)=='\\'))
43 {
44 outDir = outDir.left(l-1);
45 }
46
47 if (fileName.isEmpty()) // generate name
48 {
49 puName = "inline_umlgraph_"+QCString().setNum(umlindex);
50 baseName = outDir+"/inline_umlgraph_"+QCString().setNum(umlindex++);
51 }
52 else // user specified name
53 {
54 baseName = fileName;
55 int i=baseName.findRev('.');
56 if (i!=-1) baseName = baseName.left(i);
57 puName = baseName;
58 baseName.prepend(outDir+"/");
59 }
60
61 switch (format)
62 {
63 case PUML_BITMAP:
64 imgName =puName+".png";
65 break;
66 case PUML_EPS:
67 imgName =puName+".eps";
68 break;
69 case PUML_SVG:
70 imgName =puName+".svg";
71 break;
72 }
73
74 Debug::print(Debug::Plantuml,0,"*** %s baseName: %s\n","writePlantUMLSource",qPrint(baseName));
75 Debug::print(Debug::Plantuml,0,"*** %s puName: %s\n","writePlantUMLSource",qPrint(puName));
76 Debug::print(Debug::Plantuml,0,"*** %s imgName: %s\n","writePlantUMLSource",qPrint(imgName));
77
78 QCString text;
79 if (inlineCode) text = "@start"+engine+" "+imgName+"\n";
80 text.reserve(text.length()+content.length()+100); // add room for image name and end marker
81 const char *p = content.data();
82 if (p)
83 {
84 char c = 0;
85 bool insideComment = false;
86 bool initial = true;
87 while ((c=*p++))
88 {
89 text+=c;
90 switch (c)
91 {
92 case '\'': insideComment=true; break;
93 case '\n': insideComment=false; break;
94 case '\t': break;
95 case ' ': break;
96 case '@':
97 if (initial && qstrncmp(p,"start",5)==0) // @start...
98 {
99 while ((c=*p++) && isId(c)) text+=c;
100 // insert the image name
101 text+=' ';
102 text+=imgName;
103 if (c) text+=c;
104 }
105 break;
106 default:
107 if (!insideComment) initial=false;
108 break;
109 }
110 }
111 text+='\n';
112 }
113 if (inlineCode) text +="@end"+engine+"\n";
114
115 //printf("content\n====\n%s\n=====\n->\n-----\n%s\n------\n",qPrint(content),qPrint(text));
116
117 QCString qcOutDir(substitute(outDir,"\\","/"));
118 uint32_t pos = qcOutDir.findRev("/");
119 QCString generateType(qcOutDir.right(qcOutDir.length() - (pos + 1)) );
120 Debug::print(Debug::Plantuml,0,"*** %s generateType: %s\n","writePlantUMLSource",qPrint(generateType));
121 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
122 Debug::print(Debug::Plantuml,0,"*** %s generateType: %s\n","writePlantUMLSource",qPrint(generateType));
123
124 return baseName;
125}
126
127void PlantumlManager::generatePlantUMLOutput(const QCString &baseName,const QCString &/* outDir */,OutputFormat format)
128{
129 QCString imgName = baseName;
130 // The basename contains path, we need to strip the path from the filename in order
131 // to create the image file name which should be included in the index.qhp (Qt help index file).
132 int i = imgName.findRev('/');
133 if (i!=-1) // strip path
134 {
135 imgName=imgName.mid(i+1);
136 }
137 switch (format)
138 {
139 case PUML_BITMAP:
140 imgName+=".png";
141 break;
142 case PUML_EPS:
143 imgName+=".eps";
144 break;
145 case PUML_SVG:
146 imgName+=".svg";
147 break;
148 }
149
150 Doxygen::indexList->addImageFile(imgName);
151}
152
153//--------------------------------------------------------------------
154
155
157{
158 static PlantumlManager theInstance;
159 return theInstance;
160}
161
165
166static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles,
167 const PlantumlManager::ContentMap &plantumlContent,
169{
170 /* example : running: java -Djava.awt.headless=true
171 -jar "/usr/local/bin/plantuml.jar"
172 -o "test_doxygen/DOXYGEN_OUTPUT/html"
173 -tpng
174 "test_doxygen/DOXYGEN_OUTPUT/html/A.pu"
175 -charset UTF-8
176 outDir:test_doxygen/DOXYGEN_OUTPUT/html
177 test_doxygen/DOXYGEN_OUTPUT/html/A
178 */
179 int exitCode = 0;
180 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
181 QCString plantumlConfigFile = Config_getString(PLANTUML_CFG_FILE);
182
183 QCString pumlExe = "java";
184 QCString pumlArgs = "";
185 QCString pumlType = "";
186 QCString pumlOutDir = "";
187
188 const StringVector &pumlIncludePathList = Config_getList(PLANTUML_INCLUDE_PATH);
189 {
190 auto it = pumlIncludePathList.begin();
191 if (it!=pumlIncludePathList.end())
192 {
193 pumlArgs += "-Dplantuml.include.path=\"";
194 pumlArgs += it->c_str();
195 ++it;
196 }
197 while (it!=pumlIncludePathList.end())
198 {
199 pumlArgs += Portable::pathListSeparator();
200 pumlArgs += it->c_str();
201 ++it;
202 }
203 }
204 if (!pumlIncludePathList.empty()) pumlArgs += "\" ";
205 pumlArgs += "-Djava.awt.headless=true -jar \""+plantumlJarPath+"\" ";
206 if (!plantumlConfigFile.isEmpty())
207 {
208 pumlArgs += "-config \"";
209 pumlArgs += plantumlConfigFile;
210 pumlArgs += "\" ";
211 }
212 // the -graphvizdot option expects a relative or absolute path to the dot executable, so
213 // we need to use the unverified DOT_PATH option and check if it points to an existing file.
214 QCString dotPath = Config_getString(DOT_PATH);
215 FileInfo dp(dotPath.str());
216 if (Config_getBool(HAVE_DOT) && dp.exists() && dp.isFile())
217 {
218 pumlArgs += "-graphvizdot \"";
219 pumlArgs += dotPath;
220 pumlArgs += "\" ";
221 }
222 switch (format)
223 {
225 pumlType="png";
226 break;
228 pumlType="eps";
229 break;
231 pumlType="svg";
232 break;
233 }
234
235 {
236 for (const auto &[name,nb] : plantumlContent)
237 {
238 if (nb.content.isEmpty()) continue;
239
240 QCString pumlArguments = pumlArgs;
241 msg("Generating PlantUML %s Files in %s\n",qPrint(pumlType),name.c_str());
242 pumlArguments+="-o \"";
243 pumlArguments+=nb.outDir;
244 pumlArguments+="\" ";
245 pumlArguments+="-charset UTF-8 -t";
246 pumlArguments+=pumlType;
247 pumlArguments+=" ";
248
249 QCString puFileName("");
250 puFileName+=nb.outDir;
251 puFileName+="/";
252 pumlOutDir=puFileName;
253 puFileName+="inline_umlgraph_";
254 puFileName+=pumlType;
255 puFileName+=name.c_str();
256 puFileName+=".pu";
257
258 pumlArguments+="\"";
259 pumlArguments+=puFileName;
260 pumlArguments+="\" ";
261
262
263 QCString cachedContent;
264 FileInfo fi(puFileName.str());
265 if (fi.exists())
266 {
267 cachedContent = fileToString(puFileName);
268 }
269
270 std::ofstream file = Portable::openOutputStream(puFileName);
271 if (!file.is_open())
272 {
273 err_full(nb.srcFile,nb.srcLine,"Could not open file %s for writing",puFileName.data());
274 }
275 file.write( nb.content.data(), nb.content.length() );
276 file.close();
277 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running Plantuml arguments:%s\n",qPrint(pumlArguments));
278
279 if (cachedContent == nb.content) continue;
280
281 if ((exitCode=Portable::system(pumlExe.data(),pumlArguments.data(),TRUE))!=0)
282 {
283 err_full(nb.srcFile,nb.srcLine,"Problems running PlantUML. Verify that the command 'java -jar \"%s\" -h' works from the command line. Exit code: %d.",
284 plantumlJarPath.data(),exitCode);
285 }
286
287 if ( (format==PlantumlManager::PUML_EPS) && (Config_getBool(USE_PDFLATEX)) )
288 {
289 Debug::print(Debug::Plantuml,0,"*** %s Running epstopdf\n","PlantumlManager::runPlantumlContent");
290 auto files_kv = plantumlFiles.find(name);
291 if (files_kv!=plantumlFiles.end())
292 {
293 for (const auto &str : files_kv->second)
294 {
295 const int maxCmdLine = 40960;
297 epstopdfArgs.sprintf("\"%s%s.eps\" --outfile=\"%s%s.pdf\"",
298 pumlOutDir.data(),str.c_str(), pumlOutDir.data(),str.c_str());
299 if ((exitCode=Portable::system("epstopdf",epstopdfArgs.data()))!=0)
300 {
301 err_full(nb.srcFile,nb.srcLine,"Problems running epstopdf. Check your TeX installation! Exit code: %d.",exitCode);
302 }
303 else
304 {
305 Dir().remove(pumlOutDir.str()+str+".eps");
306 }
307 }
308 }
309 }
310 }
311 }
312}
313
321
322static void print(const PlantumlManager::FilesMap &plantumlFiles)
323{
325 {
326 for (const auto &[key,list] : plantumlFiles)
327 {
328 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Files PlantumlFiles key:%s size:%zu\n",key.c_str(),list.size());
329 for (const auto &s : list)
330 {
331 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print list:%s\n",s.c_str());
332 }
333 }
334 }
335}
336
337static void print(const PlantumlManager::ContentMap &plantumlContent)
338{
340 {
341 for (const auto &[key,content] : plantumlContent)
342 {
343 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content PlantumlContent key: %s\n",key.c_str());
344 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content:\n%s\n",content.content.data());
345 }
346 }
347}
348
350 const std::string &key, const std::string &value)
351{
352 auto kv = plantumlFiles.find(key);
353 if (kv==plantumlFiles.end())
354 {
355 kv = plantumlFiles.emplace(key,StringVector()).first;
356 }
357 kv->second.push_back(value);
358}
359
361 const std::string &key, const QCString &outDir, const QCString &puContent,
362 const QCString &srcFile,int srcLine)
363{
364 auto kv = plantumlContent.find(key);
365 if (kv==plantumlContent.end())
366 {
367 kv = plantumlContent.emplace(key,PlantumlContent("",outDir,srcFile,srcLine)).first;
368 }
369 kv->second.content+=puContent;
370}
371
372void PlantumlManager::insert(const std::string &key, const std::string &value,
373 const QCString &outDir,OutputFormat format,const QCString &puContent,
374 const QCString &srcFile,int srcLine)
375{
376 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::insert key:%s ,value:%s\n",qPrint(key),qPrint(value));
377
378 switch (format)
379 {
380 case PUML_BITMAP:
383 addPlantumlContent(m_pngPlantumlContent,key,outDir,puContent,srcFile,srcLine);
385 break;
386 case PUML_EPS:
389 addPlantumlContent(m_epsPlantumlContent,key,outDir,puContent,srcFile,srcLine);
391 break;
392 case PUML_SVG:
395 addPlantumlContent(m_svgPlantumlContent,key,outDir,puContent,srcFile,srcLine);
397 break;
398 }
399}
400
401//--------------------------------------------------------------------
@ Plantuml
Definition debug.h:38
static void print(DebugMask mask, int prio, const char *fmt,...)
Definition debug.cpp:81
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:135
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:27
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:372
ContentMap m_svgPlantumlContent
Definition plantuml.h:91
FilesMap m_pngPlantumlFiles
Definition plantuml.h:87
static PlantumlManager & instance()
Definition plantuml.cpp:156
FilesMap m_svgPlantumlFiles
Definition plantuml.h:88
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:314
void generatePlantUMLOutput(const QCString &baseName, const QCString &outDir, OutputFormat format)
Convert a PlantUML file to an image.
Definition plantuml.cpp:127
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
void msg(const char *fmt,...)
Definition message.cpp:98
#define err_full(file, line, fmt,...)
Definition message.h:89
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:166
static void addPlantumlContent(PlantumlManager::ContentMap &plantumlContent, const std::string &key, const QCString &outDir, const QCString &puContent, const QCString &srcFile, int srcLine)
Definition plantuml.cpp:360
static void print(const PlantumlManager::FilesMap &plantumlFiles)
Definition plantuml.cpp:322
static void addPlantumlFiles(PlantumlManager::FilesMap &plantumlFiles, const std::string &key, const std::string &value)
Definition plantuml.cpp:349
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
int qstrncmp(const char *str1, const char *str2, size_t len)
Definition qcstring.h:75
const char * qPrint(const char *s)
Definition qcstring.h:672
#define TRUE
Definition qcstring.h:37
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1414
A bunch of utility functions.
bool isId(int c)
Definition util.h:202