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 <mutex>
17#include "plantuml.h"
18#include "util.h"
19#include "portable.h"
20#include "config.h"
21#include "doxygen.h"
22#include "message.h"
23#include "debug.h"
24#include "fileinfo.h"
25#include "dir.h"
26#include "indexlist.h"
27#include "stringutil.h"
28
29static std::mutex g_PlantUmlMutex;
30
32 const QCString &content,OutputFormat format, const QCString &engine,
33 const QCString &srcFile,int srcLine,bool inlineCode)
34{
35 StringVector baseNameVector;
36 QCString baseName;
37 QCString puName;
38 QCString imgName;
39 QCString outDir(outDirArg);
40
41 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource fileName: {}\n",fileName);
42 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource outDir: {}\n",outDir);
43
44 // strip any trailing slashes and backslashes
45 size_t l = 0;
46 while ((l=outDir.length())>0 && (outDir.at(l-1)=='/' || outDir.at(l-1)=='\\'))
47 {
48 outDir = outDir.left(l-1);
49 }
50
51 generatePlantUmlFileNames(fileName,format,outDir,baseName,puName,imgName);
52
53 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName: {}\n",baseName);
54 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName puName: {}\n",puName);
55 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSourcebaseName imgName: {}\n",imgName);
56
57 QCString text;
58 if (inlineCode) text = "@start"+engine+" "+imgName+"\n";
59 text.reserve(text.length()+content.length()+100); // add room for image name and end marker
60 const char *p = content.data();
61 if (p)
62 {
63 char c = 0;
64 bool insideComment = false;
65 QCString locEngine;
66 while ((c=*p++))
67 {
68 text+=c;
69 switch (c)
70 {
71 case '\'': insideComment=true; break;
72 case '\n': insideComment=false; break;
73 case '\t': break;
74 case ' ': break;
75 case '@':
76 if (!insideComment && literal_at(p,"start")) // @start...
77 {
78 locEngine.clear();
79 p+=5;
80 text += "start";
81 while ((c=*p++) && isId(c))
82 {
83 locEngine += c;
84 text+=c;
85 }
86 QCString inpName;
87 QCString rest;
88
89 // skip leading whitespace
90 if (*p && (c==' ' || c=='\t'))
91 {
92 while ((c=*p++) && (c==' ' || c=='\t')) {}
93 }
94 // get everything till end or endOfLine, and split into inpName (without extension) and rest
95 enum State { InName, InExt, InRest };
96 State state = InName;
97 while (*p && c!='\n')
98 {
99 switch (state)
100 {
101 case InName: // looking for the name part
102 if (isId(c) || c=='-') { inpName+=c; }
103 else if (c=='.') { state=InExt; }
104 else { rest+=c; state=InRest; }
105 break;
106 case InExt: // skipping over extension part
107 if (!isId(c) && c!='-') { rest+=c; state=InRest; }
108 break;
109 case InRest: // gather rest until new line
110 rest+=c;
111 break;
112 }
113 c = *p++;
114 }
115 //printf("inpName='%s' rest='%s'\n",qPrint(inpName),qPrint(rest));
116 generatePlantUmlFileNames(inpName,format,outDir,baseName,puName,imgName);
117
118 // insert the image name
119 text+=' ';
120 text+=imgName;
121
122 if (!rest.isEmpty())
123 {
124 text += '\n';
125 text += rest;
126 }
127 if (c) text+=c;
128 }
129 else if (!insideComment && strncmp(p,("end"+locEngine).data(), 3+strlen(engine.data()))==0) // @end...
130 {
131 text += "end"+locEngine+"\n";
132 p+=3+locEngine.length();
133 if (!inlineCode)
134 {
135 QCString qcOutDir(substitute(outDir,"\\","/"));
136 uint32_t pos = qcOutDir.findRev("/");
137 QCString generateType(qcOutDir.right(qcOutDir.length() - (pos + 1)) );
138 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
139 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
140 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
141 baseNameVector.push_back(baseName.str());
142 text.clear();
143 }
144 }
145 break;
146 default:
147 break;
148 }
149 }
150 text+='\n';
151 }
152 if (inlineCode)
153 {
154 text +="@end"+engine+"\n";
155 //printf("content\n====\n%s\n=====\n->\n-----\n%s\n------\n",qPrint(content),qPrint(text));
156 QCString qcOutDir(substitute(outDir,"\\","/"));
157 uint32_t pos = qcOutDir.findRev("/");
158 QCString generateType(qcOutDir.right(qcOutDir.length() - (pos + 1)) );
159 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
160 PlantumlManager::instance().insert(generateType.str(),puName.str(),outDir,format,text,srcFile,srcLine);
161 Debug::print(Debug::Plantuml,0,"*** writePlantUMLSource generateType: {}\n",generateType);
162 baseNameVector.push_back(baseName.str());
163 }
164
165 return baseNameVector;
166}
167
169 QCString &baseName,QCString &puName,QCString &imgName)
170{
171 static int umlindex=1;
172
173 if (fileName.isEmpty()) // generate name
174 {
175 std::lock_guard<std::mutex> lock(g_PlantUmlMutex);
176 puName = "inline_umlgraph_"+QCString().setNum(umlindex);
177 baseName = outDir+"/inline_umlgraph_"+QCString().setNum(umlindex++);
178 }
179 else // user specified name
180 {
181 baseName = fileName;
182 int i=baseName.findRev('.');
183 if (i!=-1) baseName = baseName.left(i);
184 puName = baseName;
185 baseName.prepend(outDir+"/");
186 }
187
188 switch (format)
189 {
190 case PUML_BITMAP:
191 imgName =puName+".png";
192 break;
193 case PUML_EPS:
194 imgName =puName+".eps";
195 break;
196 case PUML_SVG:
197 imgName =puName+".svg";
198 break;
199 }
200}
201
202void PlantumlManager::generatePlantUMLOutput(const QCString &baseName,const QCString &/* outDir */,OutputFormat format,bool toIndex)
203{
204 if (!toIndex) return;
205 QCString imgName = baseName;
206 // The basename contains path, we need to strip the path from the filename in order
207 // to create the image file name which should be included in the index.qhp (Qt help index file).
208 int i = imgName.findRev('/');
209 if (i!=-1) // strip path
210 {
211 imgName=imgName.mid(i+1);
212 }
213 switch (format)
214 {
215 case PUML_BITMAP:
216 imgName+=".png";
217 break;
218 case PUML_EPS:
219 imgName+=".eps";
220 break;
221 case PUML_SVG:
222 imgName+=".svg";
223 break;
224 }
225
226 Doxygen::indexList->addImageFile(imgName);
227}
228
229//--------------------------------------------------------------------
230
231
233{
234 static PlantumlManager theInstance;
235 return theInstance;
236}
237
241
242static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles,
243 const PlantumlManager::ContentMap &plantumlContent,
245{
246 /* example : running: java -Djava.awt.headless=true
247 -jar "/usr/local/bin/plantuml.jar"
248 -o "test_doxygen/DOXYGEN_OUTPUT/html"
249 -tpng
250 "test_doxygen/DOXYGEN_OUTPUT/html/A.pu"
251 -charset UTF-8
252 outDir:test_doxygen/DOXYGEN_OUTPUT/html
253 test_doxygen/DOXYGEN_OUTPUT/html/A
254 */
255 int exitCode = 0;
256 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
257 QCString plantumlConfigFile = Config_getString(PLANTUML_CFG_FILE);
258
259 QCString pumlExe = "java";
260 QCString pumlArgs = "";
261 QCString pumlType = "";
262 QCString pumlOutDir = "";
263
264 const StringVector &pumlIncludePathList = Config_getList(PLANTUML_INCLUDE_PATH);
265 {
266 auto it = pumlIncludePathList.begin();
267 if (it!=pumlIncludePathList.end())
268 {
269 pumlArgs += "-Dplantuml.include.path=\"";
270 pumlArgs += it->c_str();
271 ++it;
272 }
273 while (it!=pumlIncludePathList.end())
274 {
275 pumlArgs += Portable::pathListSeparator();
276 pumlArgs += it->c_str();
277 ++it;
278 }
279 }
280 if (!pumlIncludePathList.empty()) pumlArgs += "\" ";
281 pumlArgs += "-Djava.awt.headless=true -jar \""+plantumlJarPath+"\" ";
282 if (!plantumlConfigFile.isEmpty())
283 {
284 pumlArgs += "-config \"";
285 pumlArgs += plantumlConfigFile;
286 pumlArgs += "\" ";
287 }
288 // the -graphvizdot option expects a relative or absolute path to the dot executable, so
289 // we need to use the unverified DOT_PATH option and check if it points to an existing file.
290 QCString dotPath = Config_getString(DOT_PATH);
291 FileInfo dp(dotPath.str());
292 if (Config_getBool(HAVE_DOT) && dp.exists() && dp.isFile())
293 {
294 pumlArgs += "-graphvizdot \"";
295 pumlArgs += dotPath;
296 pumlArgs += "\" ";
297 }
298 switch (format)
299 {
301 pumlType="png";
302 break;
304 pumlType="eps";
305 break;
307 pumlType="svg";
308 break;
309 }
310
311 {
312 for (const auto &[name,nb] : plantumlContent)
313 {
314 if (nb.content.isEmpty()) continue;
315
316 QCString pumlArguments = pumlArgs;
317 msg("Generating PlantUML {} Files in {}\n",pumlType,name);
318 pumlArguments+="-o \"";
319 pumlArguments+=nb.outDir;
320 pumlArguments+="\" ";
321 pumlArguments+="-charset UTF-8 -t";
322 pumlArguments+=pumlType;
323 pumlArguments+=" ";
324
325 QCString puFileName("");
326 puFileName+=nb.outDir;
327 puFileName+="/";
328 pumlOutDir=puFileName;
329 puFileName+="inline_umlgraph_";
330 puFileName+=pumlType;
331 puFileName+=name.c_str();
332 puFileName+=".pu";
333
334 pumlArguments+="\"";
335 pumlArguments+=puFileName;
336 pumlArguments+="\" ";
337
338
339 QCString cachedContent;
340 FileInfo fi(puFileName.str());
341 if (fi.exists())
342 {
343 cachedContent = fileToString(puFileName);
344 }
345
346 std::ofstream file = Portable::openOutputStream(puFileName);
347 if (!file.is_open())
348 {
349 err_full(nb.srcFile,nb.srcLine,"Could not open file {} for writing",puFileName);
350 }
351 file.write( nb.content.data(), nb.content.length() );
352 file.close();
353 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running Plantuml arguments:{}\n",pumlArguments);
354
355 if (cachedContent == nb.content) continue;
356
357 if ((exitCode=Portable::system(pumlExe.data(),pumlArguments.data(),TRUE))!=0)
358 {
359 err_full(nb.srcFile,nb.srcLine,"Problems running PlantUML. Verify that the command 'java -jar \"{}\" -h' works from the command line. Exit code: {}.",
360 plantumlJarPath,exitCode);
361 }
362
363 if ( (format==PlantumlManager::PUML_EPS) && (Config_getBool(USE_PDFLATEX)) )
364 {
365 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::runPlantumlContent Running epstopdf\n");
366 auto files_kv = plantumlFiles.find(name);
367 if (files_kv!=plantumlFiles.end())
368 {
369 for (const auto &str : files_kv->second)
370 {
371 const int maxCmdLine = 40960;
373 epstopdfArgs.sprintf("\"%s%s.eps\" --outfile=\"%s%s.pdf\"",
374 pumlOutDir.data(),str.c_str(), pumlOutDir.data(),str.c_str());
375 if ((exitCode=Portable::system("epstopdf",epstopdfArgs.data()))!=0)
376 {
377 err_full(nb.srcFile,nb.srcLine,"Problems running epstopdf. Check your TeX installation! Exit code: {}.",exitCode);
378 }
379 else
380 {
381 Dir().remove(pumlOutDir.str()+str+".eps");
382 }
383 }
384 }
385 }
386 }
387 }
388}
389
397
398static void print(const PlantumlManager::FilesMap &plantumlFiles)
399{
401 {
402 for (const auto &[key,list] : plantumlFiles)
403 {
404 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Files PlantumlFiles key:{} size:{}\n",key,list.size());
405 for (const auto &s : list)
406 {
407 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print list:{}\n",s);
408 }
409 }
410 }
411}
412
413static void print(const PlantumlManager::ContentMap &plantumlContent)
414{
416 {
417 for (const auto &[key,content] : plantumlContent)
418 {
419 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content PlantumlContent key: {}\n",key);
420 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::print Content:\n{}\n",content.content);
421 }
422 }
423}
424
426 const std::string &key, const std::string &value)
427{
428 auto kv = plantumlFiles.find(key);
429 if (kv==plantumlFiles.end())
430 {
431 kv = plantumlFiles.emplace(key,StringVector()).first;
432 }
433 kv->second.push_back(value);
434}
435
437 const std::string &key, const QCString &outDir, const QCString &puContent,
438 const QCString &srcFile,int srcLine)
439{
440 auto kv = plantumlContent.find(key);
441 if (kv==plantumlContent.end())
442 {
443 kv = plantumlContent.emplace(key,PlantumlContent("",outDir,srcFile,srcLine)).first;
444 }
445 kv->second.content+=puContent;
446}
447
448void PlantumlManager::insert(const std::string &key, const std::string &value,
449 const QCString &outDir,OutputFormat format,const QCString &puContent,
450 const QCString &srcFile,int srcLine)
451{
452 Debug::print(Debug::Plantuml,0,"*** PlantumlManager::insert key:{} ,value:{}\n",key,value);
453
454 switch (format)
455 {
456 case PUML_BITMAP:
459 addPlantumlContent(m_pngPlantumlContent,key,outDir,puContent,srcFile,srcLine);
461 break;
462 case PUML_EPS:
465 addPlantumlContent(m_epsPlantumlContent,key,outDir,puContent,srcFile,srcLine);
467 break;
468 case PUML_SVG:
471 addPlantumlContent(m_svgPlantumlContent,key,outDir,puContent,srcFile,srcLine);
473 break;
474 }
475}
476
477//--------------------------------------------------------------------
@ Plantuml
Definition debug.h:39
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:133
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
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:132
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:93
std::map< std::string, PlantumlContent > ContentMap
Definition plantuml.h:77
OutputFormat
Plant UML output image formats.
Definition plantuml.h:44
void generatePlantUMLOutput(const QCString &baseName, const QCString &outDir, OutputFormat format, bool toIndex)
Convert a PlantUML file to an image.
Definition plantuml.cpp:202
ContentMap m_epsPlantumlContent
Definition plantuml.h:95
void generatePlantUmlFileNames(const QCString &fileName, OutputFormat format, const QCString &outDir, QCString &baseName, QCString &puName, QCString &imgName)
Definition plantuml.cpp:168
std::map< std::string, StringVector > FilesMap
Definition plantuml.h:76
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:448
StringVector 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:31
ContentMap m_svgPlantumlContent
Definition plantuml.h:94
FilesMap m_pngPlantumlFiles
Definition plantuml.h:90
static PlantumlManager & instance()
Definition plantuml.cpp:232
FilesMap m_svgPlantumlFiles
Definition plantuml.h:91
void run()
Run plant UML tool for all images.
Definition plantuml.cpp:390
FilesMap m_epsPlantumlFiles
Definition plantuml.h:92
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString & prepend(const char *s)
Definition qcstring.h:422
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
QCString right(size_t len) const
Definition qcstring.h:234
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition qcstring.h:185
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
@ ExplicitSize
Definition qcstring.h:146
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
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:172
QCString left(size_t len) const
Definition qcstring.h:229
void clear()
Definition qcstring.h:182
#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:26
#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:648
QCString pathListSeparator()
Definition portable.cpp:383
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
static void runPlantumlContent(const PlantumlManager::FilesMap &plantumlFiles, const PlantumlManager::ContentMap &plantumlContent, PlantumlManager::OutputFormat format)
Definition plantuml.cpp:242
static std::mutex g_PlantUmlMutex
Definition plantuml.cpp:29
static void addPlantumlContent(PlantumlManager::ContentMap &plantumlContent, const std::string &key, const QCString &outDir, const QCString &puContent, const QCString &srcFile, int srcLine)
Definition plantuml.cpp:436
static void print(const PlantumlManager::FilesMap &plantumlFiles)
Definition plantuml.cpp:398
static void addPlantumlFiles(PlantumlManager::FilesMap &plantumlFiles, const std::string &key, const std::string &value)
Definition plantuml.cpp:425
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:571
#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:1494
A bunch of utility functions.
bool isId(int c)
Definition util.h:256