Doxygen
Loading...
Searching...
No Matches
mermaid.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2026 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 <fstream>
18
19#include "mermaid.h"
20#include "util.h"
21#include "portable.h"
22#include "config.h"
23#include "doxygen.h"
24#include "message.h"
25#include "debug.h"
26#include "dir.h"
27#include "indexlist.h"
28
29static std::mutex g_mermaidMutex;
30static int g_mermaidIndex = 1;
31
33{
34 static MermaidManager theInstance;
35 return theInstance;
36}
37
41
43{
44 switch (format)
45 {
46 case MERM_BITMAP: return ".png";
47 case MERM_SVG: return ".svg";
48 }
49 return ".png";
50}
51
53 const QCString &content, OutputFormat format,
54 const QCString &srcFile, int srcLine)
55{
56 QCString outDir(outDirArg);
57 QCString baseName;
58
59 // strip any trailing slashes and backslashes
60 while (!outDir.isEmpty() && (outDir.at(outDir.length()-1)=='/' || outDir.at(outDir.length()-1)=='\\'))
61 {
62 outDir = outDir.left(outDir.length()-1);
63 }
64
65 if (fileName.isEmpty())
66 {
67 std::lock_guard<std::mutex> lock(g_mermaidMutex);
68 baseName = outDir + "/inline_mermaid_" + QCString().setNum(g_mermaidIndex++);
69 }
70 else
71 {
72 baseName = fileName;
73 int i = baseName.findRev('.');
74 if (i != -1) baseName = baseName.left(i);
75 baseName.prepend(outDir + "/");
76 }
77
78 QCString mmdName = baseName + ".mmd";
79
80 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource baseName: {}\n", baseName);
81 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource mmdName: {}\n", mmdName);
82
83 // Write the .mmd source file
84 std::ofstream file = Portable::openOutputStream(mmdName);
85 if (!file.is_open())
86 {
87 err_full(srcFile, srcLine, "Could not open file {} for writing", mmdName);
88 return baseName;
89 }
90 file.write(content.data(), content.length());
91 file.close();
92
93 // Store for batch processing in run()
94 MermaidContent mc(baseName, content, outDir, srcFile, srcLine);
95 switch (format)
96 {
97 case MERM_BITMAP: m_pngContent.push_back(mc); break;
98 case MERM_SVG: m_svgContent.push_back(mc); break;
99 }
100
101 return baseName;
102}
103
104void MermaidManager::generateMermaidOutput(const QCString &baseName, const QCString &/*outDir*/,
105 OutputFormat format)
106{
107 QCString imgName = baseName;
108 int i = imgName.findRev('/');
109 if (i != -1)
110 {
111 imgName = imgName.mid(i + 1);
112 }
113 imgName += imageExtension(format);
114 Doxygen::indexList->addImageFile(imgName);
115}
116
117static void runMermaidContent(const MermaidManager::ContentList &contentList,
119{
120 if (contentList.empty()) return;
121
122 QCString mermaidPath = Config_getString(MERMAID_PATH);
123 if (mermaidPath.isEmpty()) return;
124
125 QCString mmdc = mermaidPath;
126 if (!mmdc.isEmpty() && mmdc.at(mmdc.length()-1) != '/' && mmdc.at(mmdc.length()-1) != '\\')
127 {
128 mmdc += "/";
129 }
130 mmdc += "mmdc";
131
132 QCString mermaidConfigFile = Config_getString(MERMAID_CONFIG_FILE);
133
134 QCString ext;
135 switch (format)
136 {
137 case MermaidManager::MERM_BITMAP: ext = "png"; break;
138 case MermaidManager::MERM_SVG: ext = "svg"; break;
139 }
140
141 for (const auto &mc : contentList)
142 {
143 if (mc.content.isEmpty()) continue;
144
145 QCString inputFile = mc.baseName + ".mmd";
146 QCString outputFile = mc.baseName + "." + ext;
147
148 // Check if content has changed since last run (caching)
149 FileInfo fi(outputFile.str());
150 if (fi.exists())
151 {
152 QCString cachedContent = fileToString(inputFile);
153 if (cachedContent == mc.content) continue;
154 }
155
156 // Build the mmdc command arguments
157 QCString args;
158 args += "-i \"" + inputFile + "\" ";
159 args += "-o \"" + outputFile + "\" ";
160
161 if (!mermaidConfigFile.isEmpty())
162 {
163 args += "-c \"" + mermaidConfigFile + "\" ";
164 }
165
166 msg("Generating Mermaid {} file {}\n", ext, outputFile);
167 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run Running: {} {}\n", mmdc, args);
168
169 int exitCode = Portable::system(mmdc.data(), args.data(), TRUE);
170 if (exitCode != 0)
171 {
172 err_full(mc.srcFile, mc.srcLine,
173 "Problems running Mermaid (mmdc). Verify that the command '{}' works from the command line. Exit code: {}.",
174 mmdc, exitCode);
175 }
176 }
177}
178
@ Mermaid
Definition debug.h:51
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
static IndexList * indexList
Definition doxygen.h:132
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:179
OutputFormat
Mermaid output image formats.
Definition mermaid.h:44
QCString writeMermaidSource(const QCString &outDirArg, const QCString &fileName, const QCString &content, OutputFormat format, const QCString &srcFile, int srcLine)
Write a Mermaid source file and register it for CLI rendering.
Definition mermaid.cpp:52
void generateMermaidOutput(const QCString &baseName, const QCString &outDir, OutputFormat format)
Register a generated Mermaid image with the index.
Definition mermaid.cpp:104
ContentList m_pngContent
Definition mermaid.h:78
static MermaidManager & instance()
Definition mermaid.cpp:32
ContentList m_svgContent
Definition mermaid.h:79
static QCString imageExtension(OutputFormat format)
Definition mermaid.cpp:42
std::vector< MermaidContent > ContentList
Definition mermaid.h:71
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
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
#define Config_getString(name)
Definition config.h:32
static std::mutex g_mermaidMutex
Definition mermaid.cpp:29
static int g_mermaidIndex
Definition mermaid.cpp:30
static void runMermaidContent(const MermaidManager::ContentList &contentList, MermaidManager::OutputFormat format)
Definition mermaid.cpp:117
#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
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
Portable versions of functions that are platform dependent.
#define TRUE
Definition qcstring.h:37
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
A bunch of utility functions.