/****************************************************************************** * * Copyright (C) 1997-2019 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. * */ #include #include "dotfilepatcher.h" #include "dotrunner.h" #include "config.h" #include "message.h" #include "docparser.h" #include "docnode.h" #include "doxygen.h" #include "util.h" #include "dot.h" #include "dir.h" 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" "\n" "\n" ; 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; bool targetAlreadySet = buf.find("target=")!=-1; 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.startsWith("\\ref ") || link.startsWith("@ref ")) // \ref url { result=href+"=\""; // fake ref node to resolve the url auto parser { createDocParser() }; auto dfAst { createRef( *parser.get(), link.mid(5), context ) }; auto dfAstImpl = dynamic_cast(dfAst.get()); const DocRef *df = std::get_if(&dfAstImpl->root); result+=externalRef(relPath,df->ref(),TRUE); if (!df->file().isEmpty()) result += addHtmlExtensionIfMissing(df->file()); if (!df->anchor().isEmpty()) result += "#" + df->anchor(); 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(true); if (!result.isEmpty())targetAlreadySet=true; } result+= href+"=\""; result+=externalRef(relPath,ref,TRUE); result+= url + "\""; } else // should not happen, but handle properly anyway { result = href+"=\"" + link + "\""; } } if (!target.isEmpty() && !targetAlreadySet) { result+=" target=\""+target+"\""; } QCString leftPart = buf.left(indexS); QCString rightPart = buf.mid(indexE+1); //printf("replaceRef(\n'%s'\n)->\n'%s+%s+%s'\n", // qPrint(buf),qPrint(leftPart),qPrint(result),qPrint(rightPart)); 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. */ bool DotFilePatcher::convertMapFile(TextStream &t,const QCString &mapName, const QCString &relPath, bool urlOnly, const QCString &context) { std::ifstream f(mapName.str(),std::ifstream::in); if (!f.is_open()) { err("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",qPrint(mapName)); return FALSE; } std::string line; while (getline(f,line)) // foreach line { QCString buf = line+'\n'; if (buf.startsWith("0 && (indexE=replBuf.find('"',indexS+4))!=-1) { t << replBuf.left(indexS-1) << replBuf.right(replBuf.length() - indexE - 1); } else { t << replBuf; } } } return TRUE; } DotFilePatcher::DotFilePatcher(const QCString &patchFile) : m_patchFile(patchFile) { } bool DotFilePatcher::isSVGFile() const { return m_patchFile.endsWith(".svg"); } int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath, bool urlOnly,const QCString &context,const QCString &label) { size_t id = m_maps.size(); m_maps.emplace_back(mapFile,relPath,urlOnly,context,label); return static_cast(id); } int DotFilePatcher::addFigure(const QCString &baseName, const QCString &figureName,bool heightCheck) { size_t id = m_maps.size(); m_maps.emplace_back(figureName,"",heightCheck,"",baseName); return static_cast(id); } int DotFilePatcher::addSVGConversion(const QCString &relPath,bool urlOnly, const QCString &context,bool zoomable, int graphId) { size_t id = m_maps.size(); m_maps.emplace_back("",relPath,urlOnly,context,"",zoomable,graphId); return static_cast(id); } int DotFilePatcher::addSVGObject(const QCString &baseName, const QCString &absImgName, const QCString &relPath) { size_t id = m_maps.size(); m_maps.emplace_back(absImgName,relPath,false,"",baseName); return static_cast(id); } bool DotFilePatcher::run() const { //printf("DotFilePatcher::run(): %s\n",qPrint(m_patchFile)); bool interactiveSVG_local = Config_getBool(INTERACTIVE_SVG); bool isSVGFile = m_patchFile.endsWith(".svg"); int graphId = -1; QCString relPath; if (isSVGFile) { const Map &map = m_maps.front(); // there is only one 'map' for a SVG file interactiveSVG_local = interactiveSVG_local && map.zoomable; graphId = map.graphId; relPath = map.relPath; //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n", // qPrint(m_patchFile),map->zoomable); } std::string tmpName = m_patchFile.str()+".tmp"; std::string patchFile = m_patchFile.str(); Dir thisDir; if (!thisDir.rename(patchFile,tmpName)) { err("Failed to rename file %s to %s!\n",qPrint(m_patchFile),tmpName.c_str()); return FALSE; } std::ifstream fi(tmpName, std::ifstream::in); std::ofstream fo(patchFile, std::ofstream::out | std::ofstream::binary); if (!fi.is_open()) { err("problem opening file %s for patching!\n",tmpName.c_str()); thisDir.rename(tmpName,patchFile); return FALSE; } if (!fo.is_open()) { err("problem opening file %s for patching!\n",qPrint(m_patchFile)); thisDir.rename(tmpName,patchFile); return FALSE; } TextStream t(&fo); int width,height; bool insideHeader=FALSE; bool replacedHeader=FALSE; bool foundSize=FALSE; int lineNr=1; std::string lineStr; static const reg::Ex reSVG(R"([\[<]!-- SVG [0-9]+)"); static const reg::Ex reMAP(R"(\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 << "