/*****
* texfile.h
* John Bowman 2003/03/14
*
* Encapsulates the writing of commands to a TeX file.
*****/
#ifndef TEXFILE_H
#define TEXFILE_H
#include <fstream>
#include <iomanip>
#include <iostream>
#include "common.h"
#include "pair.h"
#include "bbox.h"
#include "pen.h"
#include "util.h"
#include "interact.h"
#include "path.h"
#include "array.h"
#include "psfile.h"
#include "settings.h"
#include "asyprocess.h"
namespace camp {
template<class T>
void texdocumentclass(T& out, bool pipe=false)
{
if(settings::latex(settings::getSetting<string>("tex")) &&
(pipe || !settings::getSetting<bool>("inlinetex")))
out << "\\documentclass[12pt]{article}" << newl;
}
template<class T>
void texuserpreamble(T& out,
mem::list<string>& preamble=processData().TeXpreamble,
bool pipe=false)
{
for(mem::list<string>::iterator p=preamble.begin(); p != preamble.end();
++p) {
out << stripblanklines(*p);
if(pipe) out << newl << newl;
}
}
template<class T>
void latexfontencoding(T& out)
{
out << "\\makeatletter%" << newl
<< "\\let\\ASYencoding\\f@encoding%" << newl
<< "\\let\\ASYfamily\\f@family%" << newl
<< "\\let\\ASYseries\\f@series%" << newl
<< "\\let\\ASYshape\\f@shape%" << newl
<< "\\makeatother%" << newl;
}
std::unordered_set const latexCharacters = {'#', '$', '%', '&', '\\', '^', '_', '{', '}', '~'};
template<class T>
void texpreamble(T& out, mem::list<string>& preamble=processData().TeXpreamble,
bool pipe=false, bool ASYbox=true)
{
texuserpreamble(out,preamble,pipe);
string texengine=settings::getSetting<string>("tex");
string outPath=stripFile(settings::outname());
if(settings::context(texengine))
out << "\\disabledirectives[system.errorcontext]%" << newl;
if(ASYbox)
out << "\\newbox\\ASYbox" << newl
<< "\\newdimen\\ASYdimen" << newl;
out << "\\def\\ASYprefix{" << escapeCharacters(outPath, latexCharacters) << "}" << newl
<< "\\long\\def\\ASYbase#1#2{\\leavevmode\\setbox\\ASYbox=\\hbox{#1}%"
<< "\\ASYdimen=\\ht\\ASYbox%" << newl
<< "\\setbox\\ASYbox=\\hbox{#2}\\lower\\ASYdimen\\box\\ASYbox}" << newl;
if(!pipe)
out << "\\long\\def\\ASYaligned(#1,#2)(#3,#4)#5#6#7{\\leavevmode%" << newl
<< "\\setbox\\ASYbox=\\hbox{#7}%" << newl
<< "\\setbox\\ASYbox\\hbox{\\ASYdimen=\\ht\\ASYbox%" << newl
<< "\\advance\\ASYdimen by\\dp\\ASYbox\\kern#3\\wd\\ASYbox"
<< "\\raise#4\\ASYdimen\\box\\ASYbox}%" << newl
<< "\\setbox\\ASYbox=\\hbox{#5\\wd\\ASYbox 0pt\\dp\\ASYbox 0pt\\ht\\ASYbox 0pt\\box\\ASYbox#6}%" << newl
<< "\\hbox to 0pt{\\kern#1pt\\raise#2pt\\box\\ASYbox\\hss}}%" << newl
<< "\\long\\def\\ASYalignT(#1,#2)(#3,#4)#5#6{%" << newl
<< "\\ASYaligned(#1,#2)(#3,#4){%" << newl
<< settings::beginlabel(texengine) << "%" << newl
<< "}{%" << newl
<< settings::endlabel(texengine) << "%" << newl
<< "}{#6}}" << newl
<< "\\long\\def\\ASYalign(#1,#2)(#3,#4)#5{"
<< "\\ASYaligned(#1,#2)(#3,#4){}{}{#5}}" << newl
<< settings::rawpostscript(texengine) << newl;
}
// Work around bug in dvips.def: allow spaces in file names.
template<class T>
void dvipsfix(T &out)
{
if(!settings::pdf(settings::getSetting<string>("tex"))) {
out << "\\makeatletter" << newl
<< "\\def\\Ginclude@eps#1{%" << newl
<< " \\message{<#1>}%" << newl
<< " \\bgroup" << newl
<< " \\def\\@tempa{!}%" << newl
<< " \\dimen@\\Gin@req@width" << newl
<< " \\
[email protected]%" << newl
<< " \\divide\\dimen@\\dimen@ii" << newl
<< " \\@tempdima\\Gin@req@height" << newl
<< " \\divide\\@tempdima\\dimen@ii" << newl
<< " \\special{PSfile=#1\\space" << newl
<< " llx=\\Gin@llx\\space" << newl
<< " lly=\\Gin@lly\\space" << newl
<< " urx=\\Gin@urx\\space" << newl
<< " ury=\\Gin@ury\\space" << newl
<< " \\ifx\\Gin@scalex\\@tempa\\else rwi=\\number\\dimen@\\space\\fi" << newl
<< " \\ifx\\Gin@scaley\\@tempa\\else rhi=\\number\\@tempdima\\space\\fi" << newl
<< " \\ifGin@clip clip\\fi}%" << newl
<< " \\egroup}" << newl
<< "\\makeatother" << newl;
}
}
template<class T>
void texdefines(T& out, mem::list<string>& preamble=processData().TeXpreamble,
bool pipe=false)
{
string texengine=settings::getSetting<string>("tex");
bool latex=settings::latex(texengine);
bool inlinetex=settings::getSetting<bool>("inlinetex");
if(pipe || !inlinetex) {
bool lua=settings::lua(texengine);
if(latex) {
if(lua) {
out << "\\edef\\pdfpageattr{\\pdfvariable pageattr}" << newl
<< "\\ifx\\pdfpagewidth\\undefined\\let\\pdfpagewidth\\paperwidth"
<< "\\fi" << newl
<< "\\ifx\\pdfpageheight\\undefined\\let\\pdfpageheight"
<< "\\paperheight"
<< "\\fi" << newl
<< "\\usepackage{graphicx}" << newl;
} else {
out << "\\let\\paperwidthsave\\paperwidth\\let\\paperwidth\\undefined"
<< newl
<< "\\usepackage{graphicx}" << newl
<< "\\let\\paperwidth\\paperwidthsave" << newl;
}
} else {
if(lua) {
out << "\\edef\\pdfpageattr{\\pdfvariable pageattr}" << newl
<< "\\ifx\\pdfpagewidth\\undefined\\let\\pdfpagewidth\\pagewidth"
<< "\\fi" << newl
<< "\\ifx\\pdfpageheight\\undefined\\let\\pdfpageheight"
<< "\\pageheight"
<< "\\fi" << newl;
}
}
texpreamble(out,preamble,pipe);
}
if(pipe) {
// Make tex pipe aware of a previously generated aux file.
string name=auxname(settings::outname(),"aux");
std::ifstream fin(name.c_str());
if(fin) {
std::ofstream fout("texput.aux");
string s;
while(getline(fin,s))
fout << s << endl;
}
}
if(latex) {
if(!inlinetex) {
dvipsfix(out);
}
if(pipe) {
out << "\\begin{document}" << newl;
latexfontencoding(out);
}
} else if(!settings::context(texengine)) {
out << "\\input graphicx" << newl // Fix miniltx path parsing bug:
<< "\\makeatletter" << newl
<< "\\def\\filename@parse#1{%" << newl
<< " \\let\\filename@area\\@empty" << newl
<< " \\expandafter\\filename@path#1/\\\\}" << newl
<< "\\def\\filename@path#1/#2\\\\{%" << newl
<< " \\ifx\\\\#2\\\\%" << newl
<< " \\def\\reserved@a{\\filename@simple#1.\\\\}%" << newl
<< " \\else" << newl
<< " \\edef\\filename@area{\\filename@area#1/}%" << newl
<< " \\def\\reserved@a{\\filename@path#2\\\\}%" << newl
<< " \\fi" << newl
<< " \\reserved@a}" << newl
<< "\\makeatother" << newl;
dvipsfix(out);
if(!pipe)
out << "\\input picture" << newl;
}
}
template<class T>
bool setlatexfont(T& out, const pen& p, const pen& lastpen)
{
if(p.size() != lastpen.size() || p.Lineskip() != lastpen.Lineskip()) {
out << "\\fontsize{" << p.size()*settings::ps2tex << "}{"
<< p.Lineskip()*settings::ps2tex << "}\\selectfont%" << newl;
return true;
}
return false;
}
template<class T>
bool settexfont(T& out, const pen& p, const pen& lastpen, bool latex)
{
string font=p.Font();
if(font != lastpen.Font() || (!latex && p.size() != lastpen.size())) {
out << font << "%" << newl;
return true;
}
return false;
}
class texfile : public psfile {
protected:
bbox box;
bool inlinetex;
double Hoffset;
int level;
bool pdf;
public:
string texengine;
texfile(const string& texname, const bbox& box, bool pipe=false);
virtual ~texfile();
void prologue(bool deconstruct=false);
virtual void beginpage() {}
void epilogue(bool pipe=false);
virtual void endpage() {}
void setpen(pen p);
void setfont(pen p);
void gsave(bool tex=true);
void grestore(bool tex=true);
void beginspecial();
void endspecial();
void special(const string &s);
void beginraw();
void endraw();
void begingroup() {++level;}
void endgroup() {--level;}
bool toplevel() {return level == 0;}
virtual void beginpicture(const bbox& b);
void endpicture(const bbox& b, bool newPage=false);
virtual void newpage(const bbox&) {
verbatimline(settings::newpage(texengine));
}
void BBox(const bbox& b) {
bbox B=b.shift(pair(-hoffset(),-voffset()));
if(pdf) {
if(settings::xe(texengine))
*out << "\\special{pdf: put @thispage <</MediaBox [" << B << "]>>}%"
<< newl;
else
if(settings::context(texengine)) {
double width=B.right-B.left;
double height=B.top-B.bottom;
*out << "\\definepapersize[asy]["
<< "width=" << width << "bp,"
<< "height=" << height << "bp]%" << newl
<< "\\setuppapersize[asy][asy]%" << newl
<< "\\setuplayout["
<< "backspace=" << -B.left << "bp,"
<< "topspace=" << B.top-(box.top-box.bottom) << "bp]%" << newl;
} else
*out << "\\pdfpageattr{/MediaBox [" << B << "]}%" << newl;
}
}
void writepair(pair z) {
*out << z;
}
void miniprologue();
void writeshifted(path p, bool newPath=true);
virtual double hoffset() {return Hoffset;}
virtual double voffset() {return box.bottom;}
// Draws label transformed by T at position z.
void put(const string& label, const transform& T, const pair& z,
const pair& Align);
void beginlayer(string psname, bool postscript);
void endlayer();
virtual void Offset(const bbox& box, bool special=false) {};
};
class svgtexfile : public texfile {
mem::stack<size_t> clipstack;
size_t clipcount;
size_t gradientcount;
size_t gouraudcount;
size_t tensorcount;
bool inspecial;
static string nl;
pair offset;
bool first;
bool deconstruct;
public:
svgtexfile(const string& texname, const bbox& box, bool pipe=false,
bool deconstruct=false) :
texfile(texname,box,pipe), deconstruct(deconstruct) {
inspecial=false;
*out << "\\catcode`\\%=12" << newl
<< "\\def\\percent{%}" << newl
<< "\\catcode`\\%=14" << newl;
first=true;
Offset(box);
}
void Offset(const bbox& b, bool special=false) {
box=b;
if(special) {
texfile::beginpicture(b);
pair bboxshift=pair(-2*b.left,b.top-b.bottom);
bbox b0=svgbbox(b,bboxshift);
*out << "\\special{dvisvgm:bbox f "
<< b0.left << "bp "
<< b0.bottom << "bp "
<< b0.right << "bp "
<< b0.top << "bp}%" << newl;
}
Hoffset=inlinetex ? box.right : box.left;
offset=pair(box.left,box.top);
clipstack=mem::stack<size_t>();
clipcount=0;
gradientcount=0;
gouraudcount=0;
tensorcount=0;
}
void beginpicture(const bbox& b) {
Offset(b,true);
}
void newpage(const bbox& b) {
if(deconstruct) {
if(first)
first=false;
else
endpicture(b,true);
beginpicture(b);
}
}
void writeclip(path p, bool newPath=true) {
write(p,false);
}
void dot(path p, pen, bool newPath=true);
void writeshifted(pair z) {
write(conj(shift(-offset)*z)*settings::ps2tex);
}
double hoffset() {return Hoffset+offset.getx();}
double voffset() {return box.bottom+offset.gety();}
void translate(pair z) {}
void concat(transform t) {}
void beginspecial(bool def=false);
void endspecial();
void beginpage() {
beginpicture(box);
}
void endpage() {
endpicture(box);
}
void transform();
void begintransform();
void endtransform();
void clippath();
void beginpath();
void endpath();
void newpath() {
beginspecial();
begintransform();
beginpath();
}
// Workaround libc++ parsing bug under MacOS.
#ifdef __APPLE__
const string sep=" ";
#else
const string sep="";
#endif
void moveto(pair z) {
*out << sep << "M";
writeshifted(z);
}
void lineto(pair z) {
*out << sep << "L";
writeshifted(z);
}
void curveto(pair zp, pair zm, pair z1) {
*out << sep << "C";
writeshifted(zp); writeshifted(zm); writeshifted(z1);
}
void closepath() {
*out << sep << "Z";
}
string rgbhex(pen p) {
p.torgb();
return p.hex();
}
void properties(const pen& p);
void color(const pen &p, const string& type);
void stroke(const pen &p, bool dot=false);
void strokepath();
void fillrule(const pen& p, const string& type="fill");
void fill(const pen &p);
void begingradientshade(bool axial, ColorSpace colorspace,
const pen& pena, const pair& a, double ra,
const pen& penb, const pair& b, double rb);
void gradientshade(bool axial, ColorSpace colorspace,
const pen& pena, const pair& a, double ra,
bool extenda, const pen& penb, const pair& b,
double rb, bool extendb);
void gouraudshade(const pen& p0, const pair& z0,
const pen& p1, const pair& z1,
const pen& p2, const pair& z2);
void begingouraudshade(const vm::array& pens, const vm::array& vertices,
const vm::array& edges);
void gouraudshade(const pen& pentype, const vm::array& pens,
const vm::array& vertices, const vm::array& edges);
void beginclip();
void endclip(const pen &p);
void endpsclip(const pen &p) {}
void setpen(pen p) {if(!inspecial) texfile::setpen(p);}
void gsave(bool tex=false);
void grestore(bool tex=false);
};
} //namespace camp
#endif