diff --git a/README/ReleaseNotes/v642/index.md b/README/ReleaseNotes/v642/index.md index e83165d9f748d..e8ab842c5a623 100644 --- a/README/ReleaseNotes/v642/index.md +++ b/README/ReleaseNotes/v642/index.md @@ -180,6 +180,10 @@ This is not expected to affect typical usage, since the resolution model was nev ## Graphics and GUI +### Mathtext + +Drawings using TMathText can be now exported as PDF, not only PNG and PS. + ### New POLF and POLN draw options for TH2 Since ROOT 6.36 implementation of "POL" draw option was changed. Angle and radius range automatically scaled to visible histogram range filling full 2*Pi angle and full radius range. diff --git a/graf2d/graf/src/TMathText.cxx b/graf2d/graf/src/TMathText.cxx index a53390deb4418..21125cf7b89a1 100644 --- a/graf2d/graf/src/TMathText.cxx +++ b/graf2d/graf/src/TMathText.cxx @@ -45,9 +45,6 @@ Begin_Macro ../../../tutorials/visualisation/graphics/tmathtext2.C End_Macro -#### Limitation: -TMathText rendering is not implemented for the PDF output. -PostScript output should be used instead. */ const Double_t kPI = TMath::Pi(); diff --git a/graf2d/postscript/inc/TPDF.h b/graf2d/postscript/inc/TPDF.h index 81a68b2d82edd..bda38fa51a23f 100644 --- a/graf2d/postscript/inc/TPDF.h +++ b/graf2d/postscript/inc/TPDF.h @@ -95,6 +95,10 @@ class TPDF : public TVirtualPS { template void PrintPolyMarkerShape(Int_t n, T *x, T* y); + TString fFileName; ///< PDF file name + Bool_t fFontEmbed = kFALSE; ///< True is FontEmbed has been called + Bool_t fMustEmbed[29]; ///< flag to embed font + public: TPDF(); TPDF(const char *filename, Int_t type=-111); @@ -120,6 +124,10 @@ class TPDF : public TVirtualPS { void LineTo(Double_t x, Double_t y); void MoveTo(Double_t x, Double_t y); void EndObject(); + // bool FontEmbedType1(const char *filename); + bool FontEmbedType2(const char *filename); + bool FontEmbedType42(const char *filename); + void FontEmbed(); void FontEncode(); void NewObject(Int_t n); void NewPage() override; diff --git a/graf2d/postscript/src/TPDF.cxx b/graf2d/postscript/src/TPDF.cxx index e1cd9b10d10ac..506966e7f1352 100644 --- a/graf2d/postscript/src/TPDF.cxx +++ b/graf2d/postscript/src/TPDF.cxx @@ -61,6 +61,7 @@ the table of contents. #include "TROOT.h" #include "TDatime.h" #include "TColor.h" +#include "TEnv.h" #include "TVirtualPad.h" #include "TPoint.h" #include "TPoints.h" @@ -68,12 +69,15 @@ the table of contents. #include "TStyle.h" #include "TMath.h" #include "TStorage.h" +#include "TSystem.h" #include "TText.h" #include "zlib.h" #include "TObjString.h" #include "TObjArray.h" #include "snprintf.h" +#include "mathtext/fontembed.h" + // To scale fonts to the same size as the old TT version const Float_t kScale = 0.93376068; @@ -108,6 +112,7 @@ TPDF::TPDF() : TVirtualPS() fCurrentPage = kObjFirstPage; SetTitle("PDF"); gVirtualPS = this; + fFontEmbed = kFALSE; } //////////////////////////////////////////////////////////////////////////////// @@ -474,6 +479,50 @@ void TPDF::Close(Option_t *) PrintStr("@"); PrintStr("%%EOF@"); + // Embed the fonts previously used by TMathText + if (!fFontEmbed) { + // Close the file fFileName + if (fStream) { + PrintStr("@"); + CloseStream(); + } + + // Rename the file fFileName + TString tmpname = TString::Format("%s_tmp_%d", fFileName.Data(), gSystem->GetPid()); + if (gSystem->Rename(fFileName.Data(), tmpname.Data())) { + Error("Close", "Cannot open temporary file: %s", tmpname.Data()); + return; + } + + if (!OpenStream(fFileName.Data()) || gSystem->AccessPathName(fFileName.Data(), kWritePermission)) { + Error("Close", "Cannot open file: %s", fFileName.Data()); + return; + } + + // Embed the fonts at the right place + FILE *sg = fopen(tmpname.Data(), "r"); + if (!sg) { + Error("Close", "Cannot open file: %s", tmpname.Data()); + return; + } + char line[255]; + while (fgets(line, 255, sg)) { + if (strstr(line, "EndComments")) + PrintStr("%%DocumentNeededResources: ProcSet (FontSetInit)@"); + fStream->write(line, strlen(line)); + // insert font after end of generic defines + if (!fFontEmbed && strstr(line, "/ita")) { + FontEmbed(); + PrintStr("@"); + } + } + fclose(sg); + if (gSystem->Unlink(tmpname.Data())) + return; + } + + fFontEmbed = kFALSE; + // Close file stream CloseStream(); @@ -1031,6 +1080,146 @@ void TPDF::FontEncode() EndObject(); } } +//////////////////////////////////////////////////////////////////////////////// + +Bool_t TPDF::FontEmbedType2(const char *filename) +{ + std::ifstream font_file(filename, std::ios::binary); + + // We cannot read directly using iostream iterators due to + // signedness + font_file.seekg(0, std::ios::end); + + const size_t font_file_length = font_file.tellg(); + + font_file.seekg(0, std::ios::beg); + + std::vector font_data(font_file_length, '\0'); + + font_file.read(reinterpret_cast(&font_data[0]), font_file_length); + + std::string font_name; + auto pdf_string_map = mathtext::font_embed_pdf_t::font_embed_type_2(font_name, font_data); + + if (!pdf_string_map.empty()) { + // PrintRaw(postscript_string.size(), postscript_string.data()); + // PrintStr("@"); + + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// + +Bool_t TPDF::FontEmbedType42(const char *filename) +{ + std::ifstream font_file(filename, std::ios::binary); + + // We cannot read directly using iostream iterators due to signedness + + font_file.seekg(0, std::ios::end); + + const size_t font_file_length = font_file.tellg(); + + font_file.seekg(0, std::ios::beg); + + std::vector font_data(font_file_length, '\0'); + + font_file.read(reinterpret_cast(&font_data[0]), font_file_length); + + std::string font_name; + auto pdf_string_map = mathtext::font_embed_pdf_t::font_embed_type_42(font_name, font_data); + + if (!pdf_string_map.empty()) { + // PrintRaw(postscript_string.size(), postscript_string.data()); + // PrintStr("@"); + + return true; + } + fprintf(stderr, "%s:%d:\n", __FILE__, __LINE__); + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Embed font in PDF file. + +void TPDF::FontEmbed() +{ + static const char *fonttable[32][2] = {{"Root.TTFont.0", "FreeSansBold.otf"}, + {"Root.TTFont.1", "FreeSerifItalic.otf"}, + {"Root.TTFont.2", "FreeSerifBold.otf"}, + {"Root.TTFont.3", "FreeSerifBoldItalic.otf"}, + {"Root.TTFont.4", "FreeSans.otf"}, + {"Root.TTFont.5", "FreeSansOblique.otf"}, + {"Root.TTFont.6", "FreeSansBold.otf"}, + {"Root.TTFont.7", "FreeSansBoldOblique.otf"}, + {"Root.TTFont.8", "FreeMono.otf"}, + {"Root.TTFont.9", "FreeMonoOblique.otf"}, + {"Root.TTFont.10", "FreeMonoBold.otf"}, + {"Root.TTFont.11", "FreeMonoBoldOblique.otf"}, + {"Root.TTFont.12", "symbol.ttf"}, + {"Root.TTFont.13", "FreeSerif.otf"}, + {"Root.TTFont.14", "wingding.ttf"}, + {"Root.TTFont.15", "symbol.ttf"}, + {"Root.TTFont.STIXGen", "STIXGeneral.otf"}, + {"Root.TTFont.STIXGenIt", "STIXGeneralItalic.otf"}, + {"Root.TTFont.STIXGenBd", "STIXGeneralBol.otf"}, + {"Root.TTFont.STIXGenBdIt", "STIXGeneralBolIta.otf"}, + {"Root.TTFont.STIXSiz1Sym", "STIXSiz1Sym.otf"}, + {"Root.TTFont.STIXSiz1SymBd", "STIXSiz1SymBol.otf"}, + {"Root.TTFont.STIXSiz2Sym", "STIXSiz2Sym.otf"}, + {"Root.TTFont.STIXSiz2SymBd", "STIXSiz2SymBol.otf"}, + {"Root.TTFont.STIXSiz3Sym", "STIXSiz3Sym.otf"}, + {"Root.TTFont.STIXSiz3SymBd", "STIXSiz3SymBol.otf"}, + {"Root.TTFont.STIXSiz4Sym", "STIXSiz4Sym.otf"}, + {"Root.TTFont.STIXSiz4SymBd", "STIXSiz4SymBol.otf"}, + {"Root.TTFont.STIXSiz5Sym", "STIXSiz5Sym.otf"}, + {"Root.TTFont.ME", "DroidSansFallback.ttf"}, + {"Root.TTFont.CJKMing", "DroidSansFallback.ttf"}, + {"Root.TTFont.CJKCothic", "DroidSansFallback.ttf"}}; + + PrintStr("%%IncludeResource: ProcSet (FontSetInit)@"); + + // try to load font (font must be in Root.TTFontPath resource) + const char *ttpath = gEnv->GetValue("Root.TTFontPath", TROOT::GetTTFFontDir()); + + for (Int_t fontid = 1; fontid < 30; fontid++) { + if (fontid != 15 && fMustEmbed[fontid - 1]) { + TString filename = gEnv->GetValue(fonttable[fontid][0], fonttable[fontid][1]); + const char *ttfont = gSystem->FindFile(ttpath, filename, kReadPermission); + if (!ttfont) { + Error("FontEmbed", "font %d (filename '%s') not found in path", fontid, filename.Data()); + } else if (FontEmbedType2(ttfont)) { + // nothing + /*} else if(FontEmbedType1(ttfont)) { + // nothing*/ + } else if (FontEmbedType42(ttfont)) { + // nothing + } else { + Error("FontEmbed", "failed to embed font %d (filename '%s')", fontid, filename.Data()); + } + } + } + PrintStr("%%IncludeResource: font Times-Roman@"); + PrintStr("%%IncludeResource: font Times-Italic@"); + PrintStr("%%IncludeResource: font Times-Bold@"); + PrintStr("%%IncludeResource: font Times-BoldItalic@"); + PrintStr("%%IncludeResource: font Helvetica@"); + PrintStr("%%IncludeResource: font Helvetica-Oblique@"); + PrintStr("%%IncludeResource: font Helvetica-Bold@"); + PrintStr("%%IncludeResource: font Helvetica-BoldOblique@"); + PrintStr("%%IncludeResource: font Courier@"); + PrintStr("%%IncludeResource: font Courier-Oblique@"); + PrintStr("%%IncludeResource: font Courier-Bold@"); + PrintStr("%%IncludeResource: font Courier-BoldOblique@"); + PrintStr("%%IncludeResource: font Symbol@"); + PrintStr("%%IncludeResource: font ZapfDingbats@"); + + fFontEmbed = kTRUE; +} //////////////////////////////////////////////////////////////////////////////// /// Draw a line to a new position @@ -1329,6 +1518,7 @@ void TPDF::Open(const char *fname, Int_t wtype) Error("Open", "Cannot open file: %s", fname); return; } + fFileName = fname; gVirtualPS = this; diff --git a/tutorials/visualisation/graphics/tmathtext.C b/tutorials/visualisation/graphics/tmathtext.C index e677e3ace680d..b6fa30f9b858d 100644 --- a/tutorials/visualisation/graphics/tmathtext.C +++ b/tutorials/visualisation/graphics/tmathtext.C @@ -38,6 +38,7 @@ c1->Print("c1.png"); c1->Print("c1.ps"); + c1->Print("c1.pdf"); return c1; } diff --git a/tutorials/visualisation/graphics/tmathtext2.C b/tutorials/visualisation/graphics/tmathtext2.C index 613ed5ab554f9..d45e8d4cadafa 100644 --- a/tutorials/visualisation/graphics/tmathtext2.C +++ b/tutorials/visualisation/graphics/tmathtext2.C @@ -1890,5 +1890,9 @@ TCanvas *tmathtext2() l.DrawMathText(x1, y, "\\tanh"); l.DrawText(x2, y, "\\tanh"); +// c1->Print("c2.png"); +// c1->Print("c2.ps"); +// c1->Print("c2.pdf"); + return c1; }