#include "TDocDirective.h" #include "TApplication.h" #include "TClass.h" #include "TDocInfo.h" #include "TDocOutput.h" #include "TDocParser.h" #include "THtml.h" #include "TInterpreter.h" #include "TLatex.h" #include "TMacro.h" #include "TObjString.h" #include "TPRegexp.h" #include "TROOT.h" #include "TStyle.h" #include "TSystem.h" #include "TVirtualPad.h" #include "TVirtualMutex.h" #include <typeinfo> #include <fstream> #include <sstream> #include <stdlib.h> //______________________________________________________________________________ // // When THtml parses documentation (through TDocParser), it checks for special // words ("begin_something", "end_something", where the begin and end are the // significant part). THtml then searches for a TDocDirective which can handle // these tags ("whatever" in the example), passes the text enclosed by these // tags to the directive, which in turn processes it. // // That way, HTML, latex, and C++ macros can be processed by THtml, e.g. to // generate plain HTML or GIF pictures. The classes reposinsible for parsing // that are TDocHtmlDirective, TDocLatexDirective, and TDocMacroDirective, // respecively. // // Directives can have optional parameters; these are passed as paranthesis // enclosed, comma delimited name=value pairs; see SetParameters(). // // You can implement your own directive simply by deriving from TDocDirective; // the tag corresponds to TDocDirective's name (e.g. "HTML" for "begin_html" / // "end_html"). //______________________________________________________________________________ ClassImp(TDocDirective); //______________________________________________________________________________ void TDocDirective::DeleteOutputFiles(const char* ext) const { // Delete all output generated by the directive beginning // with Name() and ending with ext TString basename; GetName(basename); basename += "_"; TString dirname(GetOutputDir()); void* hDir = gSystem->OpenDirectory(dirname); const char* entry = 0; while ((entry = gSystem->GetDirEntry(hDir))) { TString sEntry(entry); if (sEntry.BeginsWith(basename) && isdigit(sEntry[basename.Length()]) && (!ext || sEntry.EndsWith(ext))) gSystem->Unlink((dirname + "/" + entry).Data()); } gSystem->FreeDirectory(hDir); } //______________________________________________________________________________ void TDocDirective::GetName(TString& name) const { // Get the full name, based on fName, fTitle, fDocParser's tag. name = fName; if (fDocParser && fDocParser->GetCurrentClass()) { name += "_"; TString outfilename; GetHtml()->GetHtmlFileName(fDocParser->GetCurrentClass(), outfilename); outfilename = gSystem->BaseName(outfilename); Ssiz_t posExt = outfilename.Last('.'); outfilename.Remove(posExt, outfilename.Length() - posExt); name += outfilename; } if (GetTitle() && strlen(GetTitle())) { name += "_"; name += GetTitle(); } if (fCounter != -1) { name += "_"; name += fCounter; } } //______________________________________________________________________________ const char* TDocDirective::GetOutputDir() const { // Get the directory for documentation output. return fHtml ? fHtml->GetOutputDir().Data() : 0; } //______________________________________________________________________________ void TDocDirective::SetParameters(const char* params) { // Given a string containing parameters in params, // we call AddParameter() for each of them. // This function splits the parameter names and // extracts their values if they are given. // Parameters are separated by ",", values are // separated from parameter names by "=". // params being // a = "a, b, c", b='d,e' // will issue two calls to AddParameter(), one for // a with value "a, b, c" and one for b with value // "d,e" (each without the quotation marks). fParameters = params; if (!fParameters.Length()) return; TString param; Ssiz_t pos = 0; while (fParameters.Tokenize(param, pos, ",")) { param = param.Strip(TString::kBoth); if (!param.Length()) continue; Ssiz_t posAssign = param.Index('='); if (posAssign != kNPOS) { TString value(param(posAssign + 1, param.Length())); value = value.Strip(TString::kBoth); if (value[0] == '\'') value = value.Strip(TString::kBoth, '\''); else if (value[0] == '"') value = value.Strip(TString::kBoth, '"'); param.Remove(posAssign, param.Length()); param = param.Strip(TString::kBoth); AddParameter(param, value); } else { param = param.Strip(TString::kBoth); AddParameter(param, 0); } } } //______________________________________________________________________________ void TDocDirective::SetParser(TDocParser* parser) { // Set the parser, and fDocOutput, fHtml from that fDocParser = parser; fDocOutput = parser ? parser->GetDocOutput() : 0; fHtml = fDocOutput? fDocOutput->GetHtml() : 0; } //______________________________________________________________________________ // // Process a "begin_html" / "end_html" block. Stop linking keywords and simply // copy the text enclosed by the directive to the output HTML file. //______________________________________________________________________________ ClassImp(TDocHtmlDirective); //______________________________________________________________________________ void TDocHtmlDirective::AddLine(const TSubString& line) { // Add a line of HTML if (line.Start() == -1) return; TPRegexp pretag("</?[pP][rR][eE][ >]"); TSubString iLine(line); Ssiz_t posPre = iLine.String().Index(pretag, iLine.Start()); if (posPre == kNPOS) fText += line; else { // remove <pre> in fVerbatim environments, and // </pre> in !fVerbatim environments. while (posPre != kNPOS && posPre > 0) { Bool_t isOpen = line[posPre + 1 - line.Start()] != '/'; Ssiz_t posClose = iLine.String().Index(">", posPre); if (posClose ==kNPOS) break; // aka oops. Ssiz_t len = posClose - posPre; if (fVerbatim) { if (isOpen) { // skip fText += iLine.String()(iLine.Start(), posPre - iLine.Start()); } else { // write it out fText += iLine.String()(iLine.Start(), posPre + len - iLine.Start()); fVerbatim = kFALSE; } } else { if (!isOpen) { // skip fText += iLine.String()(iLine.Start(), posPre - iLine.Start()); } else { // write it out fText += iLine.String()(iLine.Start(), posPre + len - iLine.Start()); fVerbatim = kTRUE; } } iLine = iLine.String()(posPre + len, iLine.Length()); posPre = iLine.String().Index(pretag, iLine.Start()); } fText += iLine; } fText += "\n"; } //______________________________________________________________________________ Bool_t TDocHtmlDirective::GetResult(TString& result) { // Set result to the HTML code that was passed in via AddLine(). // Prepend a closing </pre>, append an opening <pre> result = "</pre><!-- TDocHtmlDirective start -->"; result += fText + "<!-- TDocHtmlDirective end --><pre>"; return kTRUE; } //______________________________________________________________________________ // // Process a "begin_macro" / "end_macro" block. The block can be a file name // or a CINT script (i.e. even ".x file.C" is allowed). See AddParameter() for // supported options. Example (the quotes prevent THtml from expanding the // example): // // "BEGIN_MACRO" // .x $ROOTSYS/tutorials/hsimple.C // "END_MACRO" // // The macro is meant to create an object that can be saved as a GIF file by // calling object->SaveAs(outputfile.gif). The macro is expected to return that // object as a TObject*; if it does not, gPad is used and saved. The object // is deleted by TDocMacroDirective once saved. //______________________________________________________________________________ ClassImp(TDocMacroDirective); //______________________________________________________________________________ TDocMacroDirective::~TDocMacroDirective() { // Destructor delete fMacro; } //______________________________________________________________________________ void TDocMacroDirective::AddLine(const TSubString& line) { // Add a macro line. // Lines ending on "*HIDE*" will be executed as part of the // macro, but not shown in the source tab if the parameter // source is supplied. if (!fMacro) { TString name; GetName(name); fMacro = new TMacro(name); } // return if no line - or if there was an intentinal line-break, // i.e. an empty line if (line.Start() == -1 && const_cast<TSubString&>(line).String().Length()) return; TString sLine(line); fMacro->AddLine(sLine); fIsFilename &= !sLine.Contains('{'); } //______________________________________________________________________________ Bool_t TDocMacroDirective::GetResult(TString& result) { // Get the result (i.e. an HTML img tag) for the macro invocation. // If fShowSource is set, a second tab will be created which shows // the source. if (!fMacro) return kFALSE; if (!fMacro->GetListOfLines() || !fMacro->GetListOfLines()->First()) { Warning("GetResult", "Empty directive found!"); return kTRUE; } R__LOCKGUARD(GetHtml()->GetMakeClassMutex()); if (gDebug > 3) Info("HandleDirective_Macro", "executing macro \"%s\" with %d lines.", fMacro->GetName(), fMacro->GetListOfLines() ? fMacro->GetListOfLines()->GetEntries() + 1 : 0); Bool_t wasBatch = gROOT->IsBatch(); if (!wasBatch && !fNeedGraphics) gROOT->SetBatch(); else if (fNeedGraphics) { if (fHtml->IsBatch()) { Warning("GetResult()", "Will not initialize the graphics system; skipping macro %s!", GetName()); result = ""; return kFALSE; } gROOT->SetBatch(0); TApplication::NeedGraphicsLibs(); gApplication->InitializeGraphics(); if (gROOT->IsBatch()) { Warning("GetResult()", "Cannot initialize the graphics system; skipping macro %s!", GetName()); result = ""; return kFALSE; } } TVirtualPad* padSave = gPad; Int_t error = TInterpreter::kNoError; Long_t ret = 0; if (fIsFilename) { TString filename; TIter iLine(fMacro->GetListOfLines()); while (filename.Length() == 0) filename = ((TObjString*)iLine())->String().Strip(TString::kBoth); TString macroPath; TString modulename; if (GetHtml() && GetDocParser()) { if (GetDocParser()->GetCurrentClass()) GetHtml()->GetModuleNameForClass(modulename, GetDocParser()->GetCurrentClass()); else GetDocParser()->GetCurrentModule(modulename); } if (modulename.Length()) { GetHtml()->GetModuleMacroPath(modulename, macroPath); } else macroPath = gSystem->pwd(); const char* pathDelimiter = ":"; // use ":" even on windows TObjArray* arrDirs(macroPath.Tokenize(pathDelimiter)); TIter iDir(arrDirs); TObjString* osDir = 0; macroPath = ""; TString filenameDirPart(gSystem->DirName(filename)); filenameDirPart.Prepend('/'); // as dir delimiter, not as root dir while ((osDir = (TObjString*)iDir())) { if (osDir->String().EndsWith("\\")) osDir->String().Remove(osDir->String().Length() - 1); osDir->String() += filenameDirPart; macroPath += osDir->String() + pathDelimiter; } TString plusplus; while (filename.EndsWith("+")) { plusplus += '+'; filename.Remove(filename.Length() - 1); } TString params; if (filename.EndsWith(")")) { Ssiz_t posOpen = filename.Last('('); if (posOpen != kNPOS) { params = filename(posOpen, filename.Length()); filename.Remove(posOpen, filename.Length()); } } TString fileSysName(gSystem->BaseName(filename)); if (!gSystem->FindFile(macroPath, fileSysName)) { Error("GetResult", "Cannot find macro '%s' in path '%s'!", gSystem->BaseName(filename), macroPath.Data()); result = ""; return kFALSE; } if (fShowSource) { // copy macro into fMacro - before running it, in case the macro blocks its file std::ifstream ifMacro(fileSysName); fMacro->GetListOfLines()->Delete(); TString line; while (ifMacro) { if (!line.ReadLine(ifMacro, kFALSE) || ifMacro.eof()) break; fMacro->AddLine(line); } } fileSysName.Prepend(".x "); fileSysName += params; fileSysName += plusplus; gInterpreter->SaveContext(); gInterpreter->SaveGlobalsContext(); ret = gROOT->ProcessLine(fileSysName, &error); } else { gInterpreter->SaveContext(); gInterpreter->SaveGlobalsContext(); ret = fMacro->Exec(0, &error); } if (fShowSource) { // convert the macro source TIter iLine(fMacro->GetListOfLines()); TObjString* osLine = 0; std::stringstream ssRaw; while ((osLine = (TObjString*)iLine())) ssRaw << osLine->String() << std::endl; TDocParser *dparser = 0; if (GetDocParser()->GetCurrentClass()) dparser = new TDocParser(*(TClassDocOutput*)GetDocOutput(), GetDocParser()->GetCurrentClass()); else dparser = new TDocParser(*GetDocOutput()); std::stringstream ssConverted; dparser->Convert(ssConverted, ssRaw, "./", kTRUE /*code*/, kFALSE /*process directives*/); delete dparser; fMacro->GetListOfLines()->Delete(); TString line; while (!ssConverted.fail()) { if (!line.ReadLine(ssConverted, kFALSE) || ssConverted.eof()) break; fMacro->AddLine(line); } } Int_t sleepCycles = 50; // 50 = 5 seconds while (error == TInterpreter::kProcessing && --sleepCycles > 0) gSystem->Sleep(100); gSystem->ProcessEvents(); // in case ret needs to handle some events first if (error != TInterpreter::kNoError) Error("HandleDirective_Macro", "Error processing macro %s!", fMacro->GetName()); else if (ret) { const TObject* objRet = (const TObject*)ret; try { typeid(*objRet).name(); // needed to test whether ret is indeed an object with a vtable! objRet = dynamic_cast<const TObject*>(objRet); } catch (...) { objRet = 0; } if (objRet) { TString filename; GetName(filename); if (objRet->GetName() && strlen(objRet->GetName())) { filename += "_"; filename += objRet->GetName(); } filename.ReplaceAll(" ", "_"); result = "<span class=\"macro\"><img class=\"macro\" alt=\"output of "; result += filename; GetDocOutput()->NameSpace2FileName(filename); TString id(filename); filename += ".gif"; TString basename(filename); result += "\" title=\"MACRO\" src=\""; result += basename; result += "\" /></span>"; gSystem->PrependPathName(GetOutputDir(), filename); if (gDebug > 3) Info("HandleDirective_Macro", "Saving returned %s to file %s.", objRet->IsA()->GetName(), filename.Data()); if (fNeedGraphics) { // to get X11 to sync :-( gVirtualX->Update()/Sync() don't do it gSystem->Sleep(1000); gVirtualX->Update(0); gVirtualX->Update(1); } gSystem->ProcessEvents(); if (fNeedGraphics) { gVirtualX->Update(0); gVirtualX->Update(1); } objRet->SaveAs(filename); gSystem->ProcessEvents(); // SaveAs triggers an event // ensure objRet is not e.g. the TGMainFrame of a new TCanvas: require padSave == gPad if (objRet != gPad && padSave == gPad) delete objRet; if (fShowSource) { // TODO: we need an accessible version of the source, i.e. visible w/o javascript TString tags("</pre><div class=\"tabs\">\n" "<a id=\"" + id + "_A0\" class=\"tabsel\" href=\"" + basename + "\" onclick=\"javascript:return SetDiv('" + id + "',0);\">Picture</a>\n" "<a id=\"" + id + "_A1\" class=\"tab\" href=\"#\" onclick=\"javascript:return SetDiv('" + id + "',1);\">Source</a>\n" "<br /></div><div class=\"tabcontent\">\n" "<div id=\"" + id + "_0\" class=\"tabvisible\">" + result + "</div>\n" "<div id=\"" + id + "_1\" class=\"tabhidden\"><div class=\"listing\"><pre class=\"code\">"); TIter iLine(fMacro->GetListOfLines()); TObjString* osLine = 0; while ((osLine = (TObjString*) iLine())) if (!TString(osLine->String().Strip()).EndsWith("*HIDE*")) tags += osLine->String() + "\n"; if (tags.EndsWith("\n")) tags.Remove(tags.Length()-1); // trailing line break tags += "</pre></div></div><div class=\"clear\"></div></div><pre>"; result = tags; } } } // Remove interpreter vars first, so we can check whether we need to delete // gPad ourselves or whether it was a global var in the interpreter. gInterpreter->ResetGlobals(); gInterpreter->Reset(); if (!wasBatch) gROOT->SetBatch(kFALSE); if (padSave != gPad) { delete gPad; gPad = padSave; } // in case ret's or gPad's deletion provoke events that should be handled gSystem->ProcessEvents(); return kTRUE; } //______________________________________________________________________________ void TDocMacroDirective::AddParameter(const TString& name, const char* /*value=0*/) { // Setting fNeedGraphics if name is "GUI", // setting fShowSource if name is "SOURCE" if (!name.CompareTo("gui", TString::kIgnoreCase)) fNeedGraphics = kTRUE; else if (!name.CompareTo("source", TString::kIgnoreCase)) fShowSource = kTRUE; else Warning("AddParameter", "Unknown option %s!", name.Data()); } namespace { Float_t gLinePadding = 10.; //px Float_t gColumnPadding = 10.; //px class TLatexLine { private: std::vector<Float_t> fWidths; Float_t fHeight; TObjArray* fColumns; // of TObjString* public: TLatexLine(TObjArray* columns = 0): fHeight(0.), fColumns(columns) { if (columns) fWidths.resize(Size());} Float_t& Width(UInt_t col) {return fWidths[col];} Float_t& Height() {return fHeight;} TString* operator[](Int_t column) { if (fColumns && fColumns->GetEntriesFast() > column) return &(((TObjString*)fColumns->At(column))->String()); return 0; } UInt_t Size() const { return fColumns ? fColumns->GetEntries() : 0; } void Delete() { delete fColumns; } }; } //______________________________________________________________________________ // // Handle a "Begin_Latex"/"End_Latex" directive. // called as // "Begin_Latex(fontsize=10, separator='=,', rseparator='=|,', align=lcl)" // will create and include a TLatex-processed image, with a given fontsize // in pixels (defaults to 16). If (r)separator is given, the formulas on the // following lines will be grouped into columns; a new column starts with // (regexp) match of the separator; by default there is only one column. // separator matches any character, rseparator matches as regexp with one // column per pattern match. Only one of separator or rseparator can be given. // align defines the alignment for each columns; be default, all columns // are right aligned. NOTE that the column separator counts as a column itself! //______________________________________________________________________________ ClassImp(TDocLatexDirective); //______________________________________________________________________________ TDocLatexDirective::~TDocLatexDirective() { // Destructor gSystem->ProcessEvents(); delete fLatex; delete fBBCanvas; gSystem->ProcessEvents(); } //______________________________________________________________________________ void TDocLatexDirective::AddLine(const TSubString& line) { // Add a latex line if (line.Length() == 0) return; if (!fLatex) { TString name; GetName(name); fLatex = new TMacro(name); } TString sLine(line); GetDocParser()->Strip(sLine); if (sLine.Length() == 0) return; fLatex->AddLine(sLine); } //______________________________________________________________________________ void TDocLatexDirective::CreateLatex(const char* filename) { // Create a gif file named filename from a latex expression in fLatex. // Called when "Begin_Latex"/"End_Latex" is processed. if (!fLatex || !fLatex->GetListOfLines() || !fLatex->GetListOfLines()->First()) return; R__LOCKGUARD(GetHtml()->GetMakeClassMutex()); TVirtualPad* oldPad = gPad; Bool_t wasBatch = gROOT->IsBatch(); if (!wasBatch) gROOT->SetBatch(); const Float_t canvSize = 1200.; if (!fBBCanvas) // add magic batch vs. gui canvas sizes (4, 28) fBBCanvas = (TVirtualPad*)gROOT->ProcessLineFast( Form("new TCanvas(\"R__TDocLatexDirective_BBCanvas\",\"fBBCanvas\",%g,%g);", -(canvSize + 4.), canvSize + 28.)); if (!fBBCanvas) { Error("CreateLatex", "Cannot create a TCanvas via the interpreter!"); return; } fBBCanvas->SetBorderMode(0); fBBCanvas->SetFillColor(kWhite); gSystem->ProcessEvents(); std::list<TLatexLine> latexLines; std::vector<Float_t> maxWidth(20); UInt_t numColumns = 0; Float_t totalHeight = gLinePadding; TLatex latex; latex.SetTextFont(43); latex.SetTextSize((Float_t)fFontSize); latex.SetTextAlign(12); // calculate positions TIter iterLine(fLatex->GetListOfLines()); TObjString* line = 0; TPRegexp regexp; if (fSeparator.Length()) { if (fSepIsRegexp) regexp = TPRegexp(fSeparator); } else fSepIsRegexp = kFALSE; while ((line = (TObjString*) iterLine())) { const TString& str = line->String(); TObjArray* split = 0; if (!fSepIsRegexp) { split = new TObjArray(); split->SetOwner(); } if (!fSeparator.Length()) split->Add(new TObjString(str)); else { if (fSepIsRegexp) split = regexp.MatchS(str); else { Ssiz_t prevStart = 0; for (Ssiz_t pos = 0; pos < str.Length(); ++pos) { if (fSeparator.Index(str[pos]) != kNPOS) { split->Add(new TObjString(TString(str(prevStart, pos - prevStart)))); split->Add(new TObjString(TString(str(pos, 1)))); prevStart = pos + 1; } } split->Add(new TObjString(TString(str(prevStart, str.Length() - prevStart)))); } } latexLines.push_back(TLatexLine(split)); if (numColumns < (UInt_t)split->GetEntries()) numColumns = split->GetEntries(); Float_t heightLine = -1.; for (UInt_t col = 0; col < (UInt_t)split->GetEntries(); ++col) { Float_t widthLatex = 0.; Float_t heightLatex = 0.; TString* strCol = latexLines.back()[col]; if (strCol) GetBoundingBox(latex, *strCol, widthLatex, heightLatex); if (heightLine < heightLatex) heightLine = heightLatex; if (maxWidth.size() < col) maxWidth.resize(col * 2); if (maxWidth[col] < widthLatex) maxWidth[col] = widthLatex; latexLines.back().Width(col) = widthLatex; } latexLines.back().Height() = heightLine; totalHeight += heightLine + gLinePadding; } // while next line std::vector<Float_t> posX(numColumns + 1); for (UInt_t col = 0; col <= numColumns; ++col) { if (col == 0) posX[col] = gColumnPadding; else posX[col] = posX[col - 1] + maxWidth[col - 1] + gColumnPadding; } Float_t totalWidth = posX[numColumns]; // draw fBBCanvas->Clear(); fBBCanvas->cd(); Float_t padSizeX = totalWidth; Float_t padSizeY = totalHeight + 8.; // add magic batch vs. gui canvas sizes (4, 28) + rounding TVirtualPad* padImg = (TVirtualPad*)gROOT->ProcessLineFast( Form("new TCanvas(\"R__TDocLatexDirective_padImg\",\"padImg\",-(Int_t)%g,(Int_t)%g);", padSizeX + 4.5, padSizeY + 28.5)); padImg->SetBorderMode(0); padImg->SetFillColor(kWhite); padImg->cd(); Float_t posY = 0.; for (std::list<TLatexLine>::iterator iLine = latexLines.begin(); iLine != latexLines.end(); ++iLine) { posY += iLine->Height()/2. + gLinePadding; for (UInt_t iCol = 0; iCol < iLine->Size(); ++iCol) { TString* str = (*iLine)[iCol]; if (!str) continue; char align = 'l'; if ((UInt_t)fAlignment.Length() > iCol) align = fAlignment[(Int_t)iCol]; Float_t x = posX[iCol]; switch (align) { case 'l': break; case 'r': x += maxWidth[iCol] - iLine->Width(iCol); break; case 'c': x += 0.5*(maxWidth[iCol] - iLine->Width(iCol)); break; default: if (iLine == latexLines.begin()) Error("CreateLatex", "Invalid alignment character '%c'!", align); } latex.DrawLatex( x / padSizeX, 1. - posY / padSizeY, str->Data()); } posY += iLine->Height()/2.; } padImg->Print(filename); // delete the latex objects for (std::list<TLatexLine>::iterator iLine = latexLines.begin(); iLine != latexLines.end(); ++iLine) { iLine->Delete(); } delete padImg; if (!wasBatch) gROOT->SetBatch(kFALSE); gPad = oldPad; } //______________________________________________________________________________ void TDocLatexDirective::GetBoundingBox(TLatex& latex, const char* text, Float_t& width, Float_t& height) { // Determines the bounding box for text as height and width. // Assumes that we are in batch mode. UInt_t uiWidth = 0; UInt_t uiHeight = 0; fBBCanvas->cd(); latex.SetText(0.1, 0.5, text); latex.GetBoundingBox(uiWidth, uiHeight); width = uiWidth; height = uiHeight; } //______________________________________________________________________________ TList* TDocLatexDirective::GetListOfLines() const { // Get the list of lines as TObjStrings return fLatex ? fLatex->GetListOfLines() : 0; } //______________________________________________________________________________ Bool_t TDocLatexDirective::GetResult(TString& result) { // convert fLatex to a gif by creating a TLatex, drawing it on a // temporary canvas, and saving that to a filename in the output // directory. TString filename; GetName(filename); filename.ReplaceAll(" ", "_"); const TString& firstLine = ((TObjString*)fLatex->GetListOfLines()->First())->String(); TString latexFilename(firstLine); for (Ssiz_t namepos = 0; namepos < latexFilename.Length(); ++namepos) if (!GetDocParser()->IsWord(latexFilename[namepos])) { latexFilename.Remove(namepos, 1); --namepos; } filename += "_"; filename += latexFilename; GetDocOutput()->NameSpace2FileName(filename); filename += ".gif"; TString altText(firstLine); GetDocOutput()->ReplaceSpecialChars(altText); altText.ReplaceAll("\"", """); result = "<span class=\"latex\"><img class=\"latex\" alt=\""; result += altText; result += "\" title=\"LATEX\" src=\""; result += filename; result += "\" /></span>"; gSystem->PrependPathName(GetOutputDir(), filename); if (gDebug > 3) Info("HandleDirective_Latex", "Writing Latex \"%s\" to file %s.", fLatex->GetName(), filename.Data()); CreateLatex(filename); return kTRUE; } //______________________________________________________________________________ void TDocLatexDirective::AddParameter(const TString& name, const char* value /*=0*/) { // Parse fParameters, setting fFontSize, fAlignment, and fSeparator if (!name.CompareTo("fontsize", TString::kIgnoreCase)) { if (!value || !strlen(value)) Error("AddParameter", "Option \"fontsize\" needs a value!"); else fFontSize = atol(value); } else if (!name.CompareTo("separator", TString::kIgnoreCase)) { if (!value || !strlen(value)) Error("AddParameter", "Option \"separator\" needs a value!"); else fSeparator = value; } else if (!name.CompareTo("align", TString::kIgnoreCase)) { if (!value || !strlen(value)) Error("AddParameter", "Option \"align\" needs a value!"); else fAlignment = value; } else Warning("AddParameter", "Unknown option %s!", name.Data()); }