Doxygen
Loading...
Searching...
No Matches
dotfilepatcher.cpp
Go to the documentation of this file.
1/******************************************************************************
2*
3* Copyright (C) 1997-2019 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// own header
17#include "dotfilepatcher.h"
18
19// other includes
20#include "config.h"
21#include "dir.h"
22#include "docnode.h"
23#include "docparser.h"
24#include "dot.h"
25#include "dotrunner.h"
26#include "message.h"
27#include "portable.h"
28#include "stringutil.h"
29#include "textstream.h"
30#include "util.h"
31
32// top part of the interactive SVG header
33static const char svgZoomHeader0[] = R"svg(
34<svg id="main" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
35)svg";
36
37static const char svgZoomHeader0_noinit[] = R"svg(
38<svg id="main" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
39)svg";
40
41static const char svgZoomHeader1[] = R"svg(
42<style type="text/css"><![CDATA[
43.node, .edge {opacity: 0.7;}
44.node.selected, .edge.selected {opacity: 1;}
45.edge:hover path { stroke: red; }
46.edge:hover polygon { stroke: red; fill: red; }
47]]></style>
48)svg";
49
50// conditional part of the interactive SVG header in case the navigation panel is shown
51static const char svgZoomHeader2[] = R"svg(
52<defs>
53 <circle id="rim" cx="0" cy="0" r="7"/>
54 <circle id="rim2" cx="0" cy="0" r="3.5"/>
55 <g id="zoomPlus">
56 <use xlink:href="#rim" fill="#404040"><set attributeName="fill" to="#808080" begin="zoomplus.mouseover" end="zoomplus.mouseout"/></use>
57 <path d="M-4,0h8M0,-4v8" fill="none" stroke="white" stroke-width="1.5" pointer-events="none"/>
58 </g>
59 <g id="zoomMin">
60 <use xlink:href="#rim" fill="#404040"><set attributeName="fill" to="#808080" begin="zoomminus.mouseover" end="zoomminus.mouseout"/></use>
61 <path d="M-4,0h8" fill="none" stroke="white" stroke-width="1.5" pointer-events="none"/>
62 </g>
63 <g id="arrowUp" transform="translate(30 24)">
64 <use xlink:href="#rim"/>
65 <path pointer-events="none" fill="none" stroke="white" stroke-width="1.5" d="M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5"/>
66 </g>
67 <g id="arrowRight" transform="rotate(90) translate(36 -43)">
68 <use xlink:href="#rim"/>
69 <path pointer-events="none" fill="none" stroke="white" stroke-width="1.5" d="M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5"/>
70 </g>
71 <g id="arrowDown" transform="rotate(180) translate(-30 -48)">
72 <use xlink:href="#rim"/>
73 <path pointer-events="none" fill="none" stroke="white" stroke-width="1.5" d="M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5"/>
74 </g>
75 <g id="arrowLeft" transform="rotate(270) translate(-36 17)">
76 <use xlink:href="#rim"/>
77 <path pointer-events="none" fill="none" stroke="white" stroke-width="1.5" d="M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5"/>
78 </g>
79 <g id="resetDef">
80 <use xlink:href="#rim2" fill="#404040"><set attributeName="fill" to="#808080" begin="reset.mouseover" end="reset.mouseout"/></use>
81 </g>
82</defs>
83)svg";
84
85// part of the footer that is needed if the navigation panel is shown
86static const char svgZoomFooter1[] = R"svg(
87<g id="navigator" transform="translate(0 0)" fill="#404254">
88 <rect fill="#f2f5e9" fill-opacity="0.5" stroke="#606060" stroke-width=".5" x="0" y="0" width="60" height="60"/>
89 <use id="zoomplus" xlink:href="#zoomPlus" x="17" y="9" data-zoom="in"/>
90 <use id="zoomminus" xlink:href="#zoomMin" x="42" y="9" data-zoom="out"/>
91 <use id="reset" xlink:href="#resetDef" x="30" y="36" data-reset="true"/>
92 <use id="arrowup" xlink:href="#arrowUp" x="0" y="0" data-pan-x="0" data-pan-y="-1"/>
93 <use id="arrowright" xlink:href="#arrowRight" x="0" y="0" data-pan-x="1" data-pan-y="0"/>
94 <use id="arrowdown" xlink:href="#arrowDown" x="0" y="0" data-pan-x="0" data-pan-y="1"/>
95 <use id="arrowleft" xlink:href="#arrowLeft" x="0" y="0" data-pan-x="-1" data-pan-y="0"/>
96</g>
97<svg viewBox="0 0 15 15" width="100%" height="30px" preserveAspectRatio="xMaxYMin meet">
98 <g id="arrow_out" transform="scale(0.3 0.3)">
99 <a xlink:href="$orgname" target="_base">
100 <rect id="button" ry="5" rx="5" y="6" x="6" height="38" width="38"
101 fill="#f2f5e9" fill-opacity="0.5" stroke="#606060" stroke-width="1.0"/>
102 <path id="arrow"
103 d="M 11.500037,31.436501 C 11.940474,20.09759 22.043105,11.32322 32.158766,21.979434 L 37.068811,17.246167 C 37.068811,17.246167 37.088388,32 37.088388,32 L 22.160133,31.978069 C 22.160133,31.978069 26.997745,27.140456 26.997745,27.140456 C 18.528582,18.264221 13.291696,25.230495 11.500037,31.436501 z"
104 style="fill:#404040;"/>
105 </a>
106 </g>
107</svg>
108)svg";
109
110// generic part of the interactive SVG footer
111static const char svgZoomFooter2[] = R"svg(
112<style type='text/css'>
113<![CDATA[
114[data-mouse-over-selected='false'] { opacity: 0.7; }
115[data-mouse-over-selected='true'] { opacity: 1.0; }
116]]>
117</style>
118</svg>
119)svg";
120
121static DString replaceRef(const DString &buf,const DString &relPath,
122 bool urlOnly,const DString &context,const DString &target=DString())
123{
124 // search for href="...", store ... part in link
125 DString href = "href";
126 //bool isXLink=false;
127 int len = 6;
128 size_t indexS = buf.find("href=\""), indexE = 0;
129 bool targetAlreadySet = buf.find("target=")!=DString::npos;
130 if (indexS!=DString::npos && indexS>5 && buf.find("xlink:href=\"")!=DString::npos) // XLink href (for SVG)
131 {
132 indexS-=6;
133 len+=6;
134 href.prepend("xlink:");
135 //isXLink=true;
136 }
137 if (indexS!=DString::npos && (indexE=buf.find('"',indexS+len))!=DString::npos)
138 {
139 DString link = buf.mid(indexS+len,indexE-indexS-len);
140 DString result;
141 if (urlOnly) // for user defined dot graphs
142 {
143 if (link.startsWith("\\ref ") || link.startsWith("@ref ")) // \ref url
144 {
145 result=href+"=\"";
146 // fake ref node to resolve the url
147 auto parser { createDocParser() };
148 auto dfAst { createRef( *parser.get(), link.mid(5), context ) };
149 auto dfAstImpl = dynamic_cast<const DocNodeAST*>(dfAst.get());
150 const DocRef *df = std::get_if<DocRef>(&dfAstImpl->root);
151 result+=externalRef(relPath,df->ref());
152 if (!df->file().empty())
153 {
154 DString fn = df->file();
156 result += fn;
157 }
158 if (!df->anchor().empty())
159 {
160 result += "#" + df->anchor();
161 }
162 result += "\"";
163 }
164 else
165 {
166 result = href+"=\"" + link + "\"";
167 }
168 }
169 else // ref$url (external ref via tag file), or $url (local ref)
170 {
171 size_t marker = link.find('$');
172 if (marker!=DString::npos)
173 {
174 DString ref = link.left(marker);
175 DString url = link.mid(marker+1);
176 if (!ref.empty())
177 {
178 result = externalLinkTarget(true);
179 if (!result.empty())targetAlreadySet=true;
180 }
181 result+= href+"=\"";
182 result+=externalRef(relPath,ref);
183 result+= url + "\"";
184 }
185 else // should not happen, but handle properly anyway
186 {
187 result = href+"=\"" + link + "\"";
188 }
189 }
190 if (!target.empty() && !targetAlreadySet)
191 {
192 result+=" target=\""+target+"\"";
193 }
194 DString leftPart = buf.left(indexS);
195 DString rightPart = buf.mid(indexE+1);
196 //printf("replaceRef(\n'%s'\n)->\n'%s+%s+%s'\n",
197 // qPrint(buf),qPrint(leftPart),qPrint(result),qPrint(rightPart));
198 return leftPart + result + rightPart;
199 }
200 else
201 {
202 return buf;
203 }
204}
205
206/*! converts the rectangles in a client site image map into a stream
207* \param t the stream to which the result is written.
208* \param mapName the name of the map file.
209* \param relPath the relative path to the root of the output directory
210* (used in case CREATE_SUBDIRS is enabled).
211* \param urlOnly if false the url field in the map contains an external
212* references followed by a $ and then the URL.
213* \param context the context (file, class, or namespace) in which the
214* map file was found
215* \returns true if successful.
216*/
218 const DString &relPath, bool urlOnly,
219 const DString &context)
220{
221 std::ifstream f = Portable::openInputStream(mapName);
222 if (!f.is_open())
223 {
224 err("problems opening map file {} for inclusion in the docs!\n"
225 "If you installed Graphviz/dot after a previous failing run, \n"
226 "try deleting the output directory and rerun doxygen.\n",mapName);
227 return false;
228 }
229 std::string line;
230 while (getline(f,line)) // foreach line
231 {
232 DString buf = line+'\n';
233 if (buf.startsWith("<area"))
234 {
235 DString replBuf = replaceRef(buf,relPath,urlOnly,context);
236 // in dot version 7.0.2 the alt attribute is, incorrectly, removed.
237 // see https://gitlab.com/graphviz/graphviz/-/issues/265
238 if (size_t indexA = replBuf.find("alt="); indexA==DString::npos)
239 {
240 replBuf = replBuf.left(5) + " alt=\"\"" + replBuf.mid(5);
241 }
242
243 // strip id="..." from replBuf since the id's are not needed and not unique.
244 if (size_t indexS = replBuf.find("id=\""), indexE = 0;
245 indexS!=DString::npos && indexS>0 && (indexE=replBuf.find('"',indexS+4))!=DString::npos)
246 {
247 t << replBuf.left(indexS-1) << replBuf.mid(indexE+1);
248 }
249 else
250 {
251 t << replBuf;
252 }
253 }
254 }
255 return true;
256}
257
259 : m_patchFile(patchFile)
260{
261}
262
264{
265 return m_patchFile.endsWith(".svg");
266}
267
268int DotFilePatcher::addMap(const DString &mapFile,const DString &relPath,
269 bool urlOnly,const DString &context,const DString &label)
270{
271 size_t id = m_maps.size();
272 m_maps.emplace_back(mapFile,relPath,urlOnly,context,label);
273 return static_cast<int>(id);
274}
275
277 const DString &figureName,bool heightCheck)
278{
279 size_t id = m_maps.size();
280 m_maps.emplace_back(figureName,"",heightCheck,"",baseName);
281 return static_cast<int>(id);
282}
283
284int DotFilePatcher::addSVGConversion(const DString &relPath,bool urlOnly,
285 const DString &context,bool zoomable,
286 int graphId)
287{
288 size_t id = m_maps.size();
289 m_maps.emplace_back("",relPath,urlOnly,context,"",zoomable,graphId);
290 return static_cast<int>(id);
291}
292
294 const DString &absImgName,
295 const DString &relPath)
296{
297 size_t id = m_maps.size();
298 m_maps.emplace_back(absImgName,relPath,false,"",baseName);
299 return static_cast<int>(id);
300}
301
303{
304 //printf("DotFilePatcher::run(): %s\n",qPrint(m_patchFile));
305 bool interactiveSVG = Config_getBool(INTERACTIVE_SVG);
306 bool isSVGFile = m_patchFile.endsWith(".svg");
307 int graphId = -1;
308 DString relPath;
309 if (isSVGFile)
310 {
311 const Map &map = m_maps.front(); // there is only one 'map' for a SVG file
312 interactiveSVG = interactiveSVG && map.zoomable;
313 graphId = map.graphId;
314 relPath = map.relPath;
315 //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n",
316 // qPrint(m_patchFile),map->zoomable);
317 }
318 DString tmpName = m_patchFile+".tmp";
319 Dir thisDir;
320 if (!thisDir.rename(m_patchFile.str(),tmpName.str()))
321 {
322 err("Failed to rename file {} to {}!\n",m_patchFile,tmpName);
323 return false;
324 }
325 std::ifstream fi = Portable::openInputStream(tmpName);
326 std::ofstream fo = Portable::openOutputStream(m_patchFile);
327 if (!fi.is_open())
328 {
329 err("problem opening file {} for patching!\n",tmpName);
330 thisDir.rename(tmpName.str(),m_patchFile.str());
331 return false;
332 }
333 if (!fo.is_open())
334 {
335 err("problem opening file {} for patching!\n",m_patchFile);
336 thisDir.rename(tmpName.str(),m_patchFile.str());
337 return false;
338 }
339 TextStream t(&fo);
340 int width=0,height=0;
341 bool insideHeader=false;
342 bool replacedHeader=false;
343 bool useNagivation=false;
344 std::string lineStr;
345 static const reg::Ex reSVG(R"([\‍[<]!-- SVG [0-9]+)");
346 static const reg::Ex reMAP(R"(<!-- MAP [0-9]+)");
347 static const reg::Ex reFIG(R"(% FIG [0-9]+)");
348
349 while (getline(fi,lineStr))
350 {
351 // replace id="page0,1_graph0" with id="graph0" to work around graphviz page numbering bug
352 {
353 auto pos = lineStr.find("id=\"page0,1_graph0\"");
354 if (pos != std::string::npos)
355 lineStr.replace(pos, 19, "id=\"graph0\"");
356 }
357 DString line = lineStr+'\n';
358 //printf("line=[%s]\n",qPrint(line.stripWhiteSpace()));
359 size_t i = 0;
360 if (isSVGFile)
361 {
362 if (interactiveSVG)
363 {
364 if (line.find("<svg")!=DString::npos && !replacedHeader)
365 {
366 int count = sscanf(line.data(),"<svg width=\"%dpt\" height=\"%dpt\"",&width,&height);
367 if (count != 2) count = sscanf(line.data(),"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"%d\" height=\"%d\"",&width,&height);
368 //printf("width=%d height=%d\n",width,height);
369 useNagivation = count==2 && (width>500 || height>450);
370 insideHeader = count==2;
371 }
372 else if (insideHeader && !replacedHeader && line.find("<g id=\"graph")!=DString::npos)
373 {
374 if (useNagivation)
375 {
376 // insert special replacement header for interactive SVGs
377 t << "<!--zoomable " << height << " -->\n";
378 t << svgZoomHeader0;
379 }
380 else
381 {
383 }
384 t << svgZoomHeader1;
385 if (useNagivation)
386 {
387 t << svgZoomHeader2;
388 }
389 t << "<script type=\"application/ecmascript\" xlink:href=\"" << relPath << "svg.min.js\"/>\n";
390 t << "<svg id=\"graph\" class=\"graph\"";
391 if (useNagivation)
392 {
393 t << " data-view-width=\"" << width << "\" data-view-height=\"" << height << "\"";
394 if (graphId>=0)
395 {
396 t << " data-section-id=\"dynsection-" << graphId << "\"";
397 }
398 }
399 t << ">\n";
400
401 if (useNagivation)
402 {
403 t << "<g id=\"viewport\">\n";
404 }
405 else
406 {
407 t << line;
408 }
409 line="";
410 insideHeader=false;
411 replacedHeader=true;
412 }
413 }
414 if (!insideHeader || !useNagivation) // copy SVG and replace refs,
415 // unless we are inside the header of the SVG.
416 // Then we replace it with another header.
417 {
418 const Map &map = m_maps.front(); // there is only one 'map' for a SVG file
419 t << replaceRef(line,map.relPath,map.urlOnly,map.context,"_top");
420 }
421 }
422 else if (line.find("SVG")!=DString::npos && (i=findIndex(line.str(),reSVG))!=std::string::npos)
423 {
424 //printf("Found marker at %d\n",i);
425 int mapId=-1;
426 t << line.left(i);
427 int n = sscanf(line.data()+i+1,"!-- SVG %d",&mapId);
428 if (n==1 && mapId>=0 && mapId<static_cast<int>(m_maps.size()))
429 {
430 size_t e0 = line.find("--]");
431 size_t e1 = line.find("-->");
432 size_t e = e0!=DString::npos && e1!=DString::npos ? std::max(e0,e1) :
433 e0!=DString::npos ? e0 : e1;
434 const Map &map = m_maps.at(mapId);
435 //printf("DotFilePatcher::writeSVGFigure: file=%s zoomable=%d\n",
436 // qPrint(m_patchFile),map.zoomable);
437 if (!writeSVGFigureLink(t,map.relPath,map.label,map.mapFile))
438 {
439 err("Problem extracting size from SVG file {}\n",map.mapFile);
440 }
441 if (e!=DString::npos) t << line.mid(e+3);
442 }
443 else // error invalid map id!
444 {
445 err("Found invalid SVG id in file {}!\n",m_patchFile);
446 t << line.mid(i);
447 }
448 }
449 else if (line.find("MAP")!=DString::npos && (i=findIndex(line.str(),reMAP))!=std::string::npos)
450 {
451 int mapId=-1;
452 t << line.left(i);
453 int n = sscanf(line.data()+i,"<!-- MAP %d",&mapId);
454 if (n==1 && mapId>=0 && mapId<static_cast<int>(m_maps.size()))
455 {
456 TextStream tt;
457 const Map &map = m_maps.at(mapId);
458 //printf("patching MAP %d in file %s with contents of %s\n",
459 // mapId,qPrint(m_patchFile),qPrint(map.mapFile));
460 convertMapFile(tt,map.mapFile,map.relPath,map.urlOnly,map.context);
461 if (!tt.empty())
462 {
463 t << "<map name=\"" << mapLabelToId(map.label) << "\" id=\"" << mapLabelToId(map.label) << "\">\n";
464 t << tt.str();
465 t << "</map>\n";
466 }
467 }
468 else // error invalid map id!
469 {
470 err("Found invalid MAP id in file {}!\n",m_patchFile);
471 t << line.mid(i);
472 }
473 }
474 else if (line.find("FIG")!=DString::npos && (i=findIndex(line.str(),reFIG))!=std::string::npos)
475 {
476 int mapId=-1;
477 int n = sscanf(line.data()+i+2,"FIG %d",&mapId);
478 //printf("line='%s' n=%d\n",qPrint(line)+i,n);
479 if (n==1 && mapId>=0 && mapId<static_cast<int>(m_maps.size()))
480 {
481 const Map &map = m_maps.at(mapId);
482 //printf("patching FIG %d in file %s with contents of %s\n",
483 // mapId,qPrint(m_patchFile),qPrint(map.mapFile));
484 if (!writeVecGfxFigure(t,map.label,map.mapFile))
485 {
486 err("problem writing FIG {} figure!\n",mapId);
487 return false;
488 }
489 }
490 else // error invalid map id!
491 {
492 err("Found invalid bounding FIG {} in file {}!\n",mapId,m_patchFile);
493 t << line;
494 }
495 }
496 else
497 {
498 t << line;
499 }
500 }
501 if (isSVGFile && interactiveSVG && !useNagivation) t << "</svg>\n";
502
503 fi.close();
504 if (isSVGFile && interactiveSVG && replacedHeader)
505 {
506 DString orgName=m_patchFile.left(m_patchFile.length()-4)+"_org.svg";
507 if (useNagivation)
508 {
509 t << substitute(svgZoomFooter1,"$orgname",stripPath(orgName));
510 }
511 t << svgZoomFooter2;
512 t.flush();
513 fo.close();
514 // keep original SVG file so we can refer to it, we do need to replace
515 // dummy link by real ones
516 fi = Portable::openInputStream(tmpName);
517 fo = Portable::openOutputStream(orgName);
518 if (!fi.is_open())
519 {
520 err("problem opening file {} for reading!\n",tmpName);
521 return false;
522 }
523 if (!fo.is_open())
524 {
525 err("problem opening file {} for writing!\n",orgName);
526 return false;
527 }
528 t.setStream(&fo);
529 while (getline(fi,lineStr)) // foreach line
530 {
531 DString line = lineStr+'\n';
532 const Map &map = m_maps.front(); // there is only one 'map' for a SVG file
533 t << replaceRef(line,map.relPath,map.urlOnly,map.context,"_top");
534 }
535 t.flush();
536 fi.close();
537 fo.close();
538 }
539 // remove temporary file
540 thisDir.remove(tmpName.str());
541 return true;
542}
543
544//---------------------------------------------------------------------------------------------
545
546
547// extract size from a dot generated SVG file
548static bool readSVGSize(const DString &fileName,int *width,int *height)
549{
550 bool found=false;
551 std::ifstream f = Portable::openInputStream(fileName);
552 if (!f.is_open())
553 {
554 return false;
555 }
556 std::string line;
557 while (getline(f,line) && !found)
558 {
559 if (literal_at(line.c_str(),"<!--zoomable "))
560 {
561 *width=-1;
562 *height=-1;
563 sscanf(line.c_str(),"<!--zoomable %d",height);
564 found=true;
565 }
566 else if (sscanf(line.c_str(),"<svg width=\"%dpt\" height=\"%dpt\"",width,height)==2)
567 {
568 found=true;
569 }
570 else if (sscanf(line.c_str(),"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"%d\" height=\"%d\"",width,height)==2)
571 {
572 found=true;
573 }
574 }
575 return true;
576}
577
579{
580 out << "<p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p>";
581}
582
583/// Check if a reference to a SVG figure can be written and do so if possible.
584/// Returns false if not possible (for instance because the SVG file is not yet generated).
586 const DString &baseName,const DString &absImgName)
587{
588 int width=600,height=600;
589 if (!readSVGSize(absImgName,&width,&height))
590 {
591 return false;
592 }
593 if (width==-1)
594 {
595 if (height<=60) height=300; else height+=300; // add some extra space for zooming
596 if (height>600) height=600; // clip to maximum height of 600 pixels
597 out << "<div class=\"zoom\">";
598 //out << "<object type=\"image/svg+xml\" data=\""
599 //out << "<embed type=\"image/svg+xml\" src=\""
600 out << "<iframe scrolling=\"no\" loading=\"lazy\" frameborder=\"0\" src=\""
601 << relPath << baseName << ".svg\" width=\"100%\" height=\"" << height << "\">";
602 }
603 else
604 {
605 //out << "<object type=\"image/svg+xml\" data=\""
606 //out << "<embed type=\"image/svg+xml\" src=\""
607 out << "<iframe scrolling=\"no\" loading=\"lazy\" frameborder=\"0\" src=\""
608 << relPath << baseName << ".svg\" width=\""
609 << ((width*96+48)/72) << "\" height=\""
610 << ((height*96+48)/72) << "\">";
611 }
613 //out << "</object>";
614 //out << "</embed>";
615 out << "</iframe>";
616 if (width==-1)
617 {
618 out << "</div>";
619 }
620
621 return true;
622}
623
625 const DString &figureName)
626{
627 int width=400,height=550;
628 if (Config_getBool(USE_PDFLATEX))
629 {
630 if (!DotRunner::readBoundingBox(figureName+".pdf",&width,&height,false))
631 {
632 //printf("writeVecGfxFigure()=0\n");
633 return false;
634 }
635 }
636 else
637 {
638 if (!DotRunner::readBoundingBox(figureName+".eps",&width,&height,true))
639 {
640 //printf("writeVecGfxFigure()=0\n");
641 return false;
642 }
643 }
644 //printf("Got PDF/EPS size %d,%d\n",width,height);
645 int maxWidth = 350; /* approx. page width in points, excl. margins */
646 int maxHeight = 550; /* approx. page height in points, excl. margins */
647 out << "\\nopagebreak\n"
648 "\\begin{figure}[H]\n"
649 "\\begin{center}\n"
650 "\\leavevmode\n";
651 if (width>maxWidth || height>maxHeight) // figure too big for page
652 {
653 // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0
654 if (width*maxHeight>height*maxWidth)
655 {
656 out << "\\includegraphics[width=" << maxWidth << "pt]";
657 }
658 else
659 {
660 out << "\\includegraphics[height=" << maxHeight << "pt]";
661 }
662 }
663 else
664 {
665 out << "\\includegraphics[width=" << width << "pt]";
666 }
667
668 out << "{" << baseName << "}\n"
669 "\\end{center}\n"
670 "\\end{figure}\n";
671
672 //printf("writeVecGfxFigure()=1\n");
673 return true;
674}
675
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
DString & prepend(const char *s)
Definition dstring.h:515
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
Class representing a directory in the file system.
Definition dir.h:73
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
bool rename(const std::string &orgName, const std::string &newName, bool acceptsAbsPath=true) const
Definition dir.cpp:327
Class representing the abstract syntax tree of a documentation block.
Definition docnode.h:1471
Node representing a reference to some item.
Definition docnode.h:787
DString ref() const
Definition docnode.h:793
DString file() const
Definition docnode.h:791
DString anchor() const
Definition docnode.h:794
int addMap(const DString &mapFile, const DString &relPath, bool urlOnly, const DString &context, const DString &label)
static DString mapLabelToId(const DString &mapLabel)
bool isSVGFile() const
std::vector< Map > m_maps
DotFilePatcher(const DString &patchFile)
static bool writeVecGfxFigure(TextStream &out, const DString &baseName, const DString &figureName)
int addSVGConversion(const DString &relPath, bool urlOnly, const DString &context, bool zoomable, int graphId)
static bool writeSVGFigureLink(TextStream &out, const DString &relPath, const DString &baseName, const DString &absImgName)
Check if a reference to a SVG figure can be written and do so if possible.
static bool convertMapFile(TextStream &t, const DString &mapName, const DString &relPath, bool urlOnly=false, const DString &context=DString())
int addSVGObject(const DString &baseName, const DString &figureName, const DString &relPath)
int addFigure(const DString &baseName, const DString &figureName, bool heightCheck)
static bool readBoundingBox(const DString &fileName, int *width, int *height, bool isEps)
Text streaming class that buffers data.
Definition textstream.h:36
bool empty() const
Returns true iff the buffer is empty.
Definition textstream.h:256
void setStream(std::ostream *s)
Sets or changes the std::ostream to write to.
Definition textstream.h:67
void flush()
Flushes the buffer.
Definition textstream.h:212
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
Class representing a regular expression.
Definition regex.h:39
#define Config_getBool(name)
Definition config.h:33
IDocParserPtr createDocParser()
factory function to create a parser
Definition docparser.cpp:59
IDocNodeASTPtr createRef(IDocParser &parserIntf, const DString &target, const DString &context, const DString &srcFile, int srcLine)
static void writeSVGNotSupported(TextStream &out)
static const char svgZoomHeader1[]
static const char svgZoomHeader2[]
static DString replaceRef(const DString &buf, const DString &relPath, bool urlOnly, const DString &context, const DString &target=DString())
static const char svgZoomHeader0_noinit[]
static const char svgZoomFooter2[]
static bool readSVGSize(const DString &fileName, int *width, int *height)
static const char svgZoomHeader0[]
static const char svgZoomFooter1[]
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
#define err(fmt,...)
Definition message.h:127
std::ifstream openInputStream(const DString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:692
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
Portable versions of functions that are platform dependent.
Some helper functions for std::string.
size_t findIndex(const StringVector &sv, const std::string &s)
find the index of a string in a vector of strings, returns std::string::npos if the string could not ...
Definition stringutil.h:167
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:101
DString externalLinkTarget(const bool parent)
Definition util.cpp:4491
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
DString externalRef(const DString &relPath, const DString &ref)
Definition util.cpp:4538
DString stripPath(const DString &s)
Definition util.cpp:3960
A bunch of utility functions.