/***************************************************************************** * * $Id: dot.cpp,v 1.20 2001/03/19 19:27:40 root Exp $ * * * Copyright (C) 1997-2012 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ #ifdef _WIN32 #include #define BITMAP W_BITMAP #endif #include #include "dot.h" #include "doxygen.h" #include "message.h" #include "util.h" #include "config.h" #include "language.h" #include "defargs.h" #include "docparser.h" #include "debug.h" #include "pagedef.h" #include "portable.h" #include "dirdef.h" #include "vhdldocgen.h" #include #include #include "ftextstream.h" #include "md5.h" #include #include #include #include #define MAP_CMD "cmapx" //#define FONTNAME "Helvetica" #define FONTNAME getDotFontName() #define FONTSIZE getDotFontSize() //-------------------------------------------------------------------- static const char svgZoomHeader[] = "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" "\n" ; //-------------------------------------------------------------------- static const int maxCmdLine = 40960; /*! mapping from protection levels to color names */ static const char *normalEdgeColorMap[] = { "midnightblue", // Public "darkgreen", // Protected "firebrick4", // Private "darkorchid3", // "use" relation "grey75", // Undocumented "orange" // template relation }; static const char *normalArrowStyleMap[] = { "empty", // Public "empty", // Protected "empty", // Private "open", // "use" relation 0, // Undocumented 0 // template relation }; static const char *normalEdgeStyleMap[] = { "solid", // inheritance "dashed" // usage }; static const char *umlEdgeColorMap[] = { "midnightblue", // Public "darkgreen", // Protected "firebrick4", // Private "grey25", // "use" relation "grey75", // Undocumented "orange" // template relation }; static const char *umlArrowStyleMap[] = { "onormal", // Public "onormal", // Protected "onormal", // Private "odiamond", // "use" relation 0, // Undocumented 0 // template relation }; static const char *umlEdgeStyleMap[] = { "solid", // inheritance "solid" // usage }; /** Helper struct holding the properties of a edge in a dot graph. */ struct EdgeProperties { const char * const *edgeColorMap; const char * const *arrowStyleMap; const char * const *edgeStyleMap; }; static EdgeProperties normalEdgeProps = { normalEdgeColorMap, normalArrowStyleMap, normalEdgeStyleMap }; static EdgeProperties umlEdgeProps = { umlEdgeColorMap, umlArrowStyleMap, umlEdgeStyleMap }; static QCString getDotFontName() { static QCString dotFontName = Config_getString("DOT_FONTNAME"); if (dotFontName.isEmpty()) { //dotFontName="FreeSans.ttf"; dotFontName="Helvetica"; } return dotFontName; } static int getDotFontSize() { static int dotFontSize = Config_getInt("DOT_FONTSIZE"); if (dotFontSize<4) dotFontSize=4; return dotFontSize; } static void writeGraphHeader(FTextStream &t,const QCString &title=QCString()) { static bool interactiveSVG = Config_getBool("INTERACTIVE_SVG"); t << "digraph "; if (title.isEmpty()) { t << "\"Dot Graph\""; } else { t << "\"" << convertToXML(title) << "\""; } t << endl << "{" << endl; if (interactiveSVG) // insert a comment to force regeneration when this // option is toggled { t << " // INTERACTIVE_SVG=YES\n"; } if (Config_getBool("DOT_TRANSPARENT")) { t << " bgcolor=\"transparent\";" << endl; } t << " edge [fontname=\"" << FONTNAME << "\"," "fontsize=\"" << FONTSIZE << "\"," "labelfontname=\"" << FONTNAME << "\"," "labelfontsize=\"" << FONTSIZE << "\"];\n"; t << " node [fontname=\"" << FONTNAME << "\"," "fontsize=\"" << FONTSIZE << "\",shape=record];\n"; } static void writeGraphFooter(FTextStream &t) { t << "}" << endl; } static QCString replaceRef(const QCString &buf,const QCString relPath, bool urlOnly,const QCString &context,const QCString &target=QCString()) { // search for href="...", store ... part in link QCString href = "href"; //bool isXLink=FALSE; int len = 6; int indexS = buf.find("href=\""), indexE; if (indexS>5 && buf.find("xlink:href=\"")!=-1) // XLink href (for SVG) { indexS-=6; len+=6; href.prepend("xlink:"); //isXLink=TRUE; } if (indexS>=0 && (indexE=buf.find('"',indexS+len))!=-1) { QCString link = buf.mid(indexS+len,indexE-indexS-len); QCString result; if (urlOnly) // for user defined dot graphs { if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url { result=href+"=\""; // fake ref node to resolve the url DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context ); result+=externalRef(relPath,df->ref(),TRUE); if (!df->file().isEmpty()) result += df->file().data() + Doxygen::htmlFileExtension; if (!df->anchor().isEmpty()) result += "#" + df->anchor(); delete df; result += "\""; } else { result = href+"=\"" + link + "\""; } } else // ref$url (external ref via tag file), or $url (local ref) { int marker = link.find('$'); if (marker!=-1) { QCString ref = link.left(marker); QCString url = link.mid(marker+1); if (!ref.isEmpty()) { result = externalLinkTarget() + externalRef(relPath,ref,FALSE); } result+= href+"=\""; result+=externalRef(relPath,ref,TRUE); result+= url + "\""; } else // should not happen, but handle properly anyway { result = href+"=\"" + link + "\""; } } if (!target.isEmpty()) { result+=" target=\""+target+"\""; } QCString leftPart = buf.left(indexS); QCString rightPart = buf.mid(indexE+1); return leftPart + result + rightPart; } else { return buf; } } /*! converts the rectangles in a client site image map into a stream * \param t the stream to which the result is written. * \param mapName the name of the map file. * \param relPath the relative path to the root of the output directory * (used in case CREATE_SUBDIRS is enabled). * \param urlOnly if FALSE the url field in the map contains an external * references followed by a $ and then the URL. * \param context the context (file, class, or namespace) in which the * map file was found * \returns TRUE if successful. */ static bool convertMapFile(FTextStream &t,const char *mapName, const QCString relPath, bool urlOnly=FALSE, const QCString &context=QCString()) { QFile f(mapName); if (!f.open(IO_ReadOnly)) { err("error: problems opening map file %s for inclusion in the docs!\n" "If you installed Graphviz/dot after a previous failing run, \n" "try deleting the output directory and rerun doxygen.\n",mapName); return FALSE; } const int maxLineLen=10240; while (!f.atEnd()) // foreach line { QCString buf(maxLineLen); int numBytes = f.readLine(buf.data(),maxLineLen); buf[numBytes-1]='\0'; if (buf.left(5)==" s_newNumber; static int s_max_newNumber=0; inline int reNumberNode(int number, bool doReNumbering) { if (!doReNumbering) { return number; } else { int s = s_newNumber.size(); if (number>=s) { int ns=0; ns = s * 3 / 2 + 5; // new size if (number>=ns) // number still doesn't fit { ns = number * 3 / 2 + 5; } s_newNumber.resize(ns); for (int i=s;i0) { buf[numBytes]='\0'; const char *p = strstr(buf,bb); if (p) // found PageBoundingBox or /MediaBox string { int x,y; if (sscanf(p+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4) { //printf("readBoundingBox sscanf fail\n"); return FALSE; } return TRUE; } } else // read error! { //printf("Read error %d!\n",numBytes); return FALSE; } } err("Failed to extract bounding box from generated diagram file %s\n",fileName); return FALSE; } static bool writeVecGfxFigure(FTextStream &out,const QCString &baseName, const QCString &figureName) { int width=400,height=550; static bool usePdfLatex = Config_getBool("USE_PDFLATEX"); if (usePdfLatex) { if (!readBoundingBox(figureName+".pdf",&width,&height,FALSE)) { //printf("writeVecGfxFigure()=0\n"); return FALSE; } } else { if (!readBoundingBox(figureName+".eps",&width,&height,TRUE)) { //printf("writeVecGfxFigure()=0\n"); return FALSE; } } //printf("Got PDF/EPS size %d,%d\n",width,height); int maxWidth = 350; /* approx. page width in points, excl. margins */ int maxHeight = 550; /* approx. page height in points, excl. margins */ out << "\\nopagebreak\n" "\\begin{figure}[H]\n" "\\begin{center}\n" "\\leavevmode\n"; if (width>maxWidth || height>maxHeight) // figure too big for page { // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0 if (width*maxHeight>height*maxWidth) { out << "\\includegraphics[width=" << maxWidth << "pt]"; } else { out << "\\includegraphics[height=" << maxHeight << "pt]"; } } else { out << "\\includegraphics[width=" << width << "pt]"; } out << "{" << baseName << "}\n" "\\end{center}\n" "\\end{figure}\n"; //printf("writeVecGfxFigure()=1\n"); return TRUE; } // extract size from a dot generated SVG file static bool readSVGSize(const QCString &fileName,int *width,int *height) { bool found=FALSE; QFile f(fileName); if (!f.open(IO_ReadOnly)) { return FALSE; } const int maxLineLen=4096; char buf[maxLineLen]; while (!f.atEnd() && !found) { int numBytes = f.readLine(buf,maxLineLen-1); // read line if (numBytes>0) { buf[numBytes]='\0'; if (strncmp(buf,"\n"; t << svgZoomHeader; t << "var viewWidth = " << width << ";\n"; t << "var viewHeight = " << height << ";\n"; if (graphId>=0) { t << "var sectionId = 'dynsection-" << graphId << "';\n"; } t << "\n"; t << "