#include #include #include "modgmt_map.h" #include "modgmt_func.h" #include "modgmt_gsfuncs.h" struct input_runtime { std::list >::const_iterator ci,end; size_t pos; }; static int gs_bbox_callback(void *caller_handle, char *buf, int len) { struct input_runtime* r=static_cast(static_cast(caller_handle)->indata); size_t buflen=static_cast(len); size_t inbuf=0; if(r->ci==r->end) return 0; while(inbuf!=buflen) { if((*r->ci)->length()-r->pos>=buflen-inbuf) // Remainder of string greater or equal remainder of buffer { // Just copy part of string to buffer memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,buflen-inbuf); r->pos+=buflen-inbuf; inbuf=buflen; } else // Remainder of string lesser then remainder of buffer { // Copy remainder of string to buffer memcpy(buf+inbuf,(*r->ci)->c_str()+r->pos,(*r->ci)->length()-r->pos); inbuf+=(*r->ci)->length()-r->pos; // Go to next string r->ci++; r->pos=0; if(r->ci==r->end) break; // No more strings, leave. } } return inbuf; } static inline int eps2pdf(const std::string& eps, std::string* pdf, double res, uint ta=4, uint ga=4) { if(!gs_abilities.havepdf) return 1; // No pdf support return GhostRun("-r"+ToString(res)+" -dEPSCrop -dTextAlphaBits="+ToString(ta)+" -dGraphicAlphaBits="+ToString(ga)+" -dNOPLATFONT -dSubsetFonts=true -dEmbedAllFonts=true -dAutoFilterColorImages=false -dColorImageFilter=/FlateEncode -dAutoFilterGrayImages=false -dGrayImageFilter=/FlateEncode -dAutoFilterMonoImages=false -dMonoImageFilter=/CCITTFaxEncode -sDEVICE=pdfwrite -dMaxInlineImageSize=0 -c .setpdfwrite <> setdistillerparams",eps,0,0,pdf); } // Creating eps map from sequence of layers /* Input: If first argument is a string, then this string is a title of map (%%Title parameter in eps file). Other arguments are sequence of pairs and layers. Pairs can be x, y, xr, yr, xg, yg, xgr, ygr. x and y set shift for next layer in cm. This local shift overrides global (setted by xg, yg). xr and yr do the same but respect to global shift. xg and yg set global shift (for all subsequent layers). xgr and ygr add corresponding values to global shift. Resulted shift is added to own shift of layer (setted by function LayerShift). */ ObjectBase* GMT_Map(const ObjectList* input) { const ObjectList* list=input; auto size=list->Size(); if(0==size) return 0; decltype(size) pos=0; std::string title; std::stack > lstack; bool first=true; double xg,yg,x,y; bool xislocal=false, yislocal=false; std::list > data; xg=yg=x=y=0.0; // FIXME: Workaround ghostscript bug 202735 // See http://bugs.ghostscript.com/show_bug.cgi?id=202735 data.emplace_back(new std::string("4000 4000 translate")); data.emplace_back(new std::string(header)); while(pos l(list->At(pos)); // Descending if(l) { if(0==l->Size()) goto next; lstack.push(std::make_pair(list,pos)); list=l; pos=0; size=l->Size(); continue; } } // Check if first argument is string if(first) { first=false; OBType s(list->At(pos)); if(s) { title=s->Value(); goto next; } } // Check if argument is pair { OBType p(list->At(pos)); if(p) { std::string name=p->Name(); tolower(name); Base2Double d(p->Value()); bool suc=true; double val=d(&suc); if(!suc) goto fail; // Conversion failed suc=false; if("x"==name) {suc=true; xislocal=true; x=val;} if("y"==name) {suc=true; yislocal=true; y=val;} if("xr"==name) {suc=true; xislocal=true; x=val+xg;} if("yr"==name) {suc=true; yislocal=true; y=val+yg;} if("xg"==name) {suc=true; xg=val;} if("yg"==name) {suc=true; yg=val;} if("xgr"==name) {suc=true; xg+=val;} if("ygr"==name) {suc=true; yg+=val;} if(!suc) goto fail; // Unknown name goto next; } } // Check if argument is layer { OBType l(list->At(pos)); if(l) { struct gmt_layer layer=l->Data(); layer.shiftx+=(xislocal?x:xg); layer.shifty+=(yislocal?y:yg); xislocal=yislocal=false; if(layer.Shifted()) { data.emplace_back(new std::string(layer.BeginShift())); data.emplace_back(layer.data); data.emplace_back(new std::string(layer.EndShift())); } else data.emplace_back(layer.data); goto next; } } goto fail; // Unknown type of argument next: pos++; // Ascending if(pos==size && !lstack.empty()) { list=lstack.top().first; pos=lstack.top().second; lstack.pop(); size=list->Size(); goto next; } } data.emplace_back(new std::string(footer)); // Calculate bounding box int64_t bblx,bbly,bbrx,bbry; { std::string bboxes; struct input_runtime in={data.begin(),data.end(),0}; int ret; ret=GhostRun("-sDEVICE=bbox",gs_bbox_callback,&in,0,&bboxes,0); // Bounding box is writed on stderr if(0!=ret) goto fail; // Something wrong std::smatch m; std::smatch::const_iterator ci; std::regex r("%%BoundingBox:[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)[[:space:]]+(-?[0-9]+)"); bool ok=true; std::regex_search(bboxes,m,r); if(5!=m.size()) goto fail; // This is strange and scary ci=m.cbegin(); ci++; // Skip full match ok=ok && str2int(*ci++,&bblx); ok=ok && str2int(*ci++,&bbly); ok=ok && str2int(*ci++,&bbrx); ok=ok && str2int(*ci++,&bbry); if(!ok) goto fail; // Unexpected! // FIXME: Workaround ghostscript bug 202735 // We add 5 points to each margin because ghostscript does'nt count linewidths when calculate bounding box bblx-=4005; bbly-=4005; bbrx-=3995; bbry-=3995; } data.pop_front(); // Creating eps file { std::string curtime; { const size_t bufsize=1024; char buf[bufsize]; time_t sec; struct tm t; sec=time(0); localtime_r(&sec,&t); curtime.assign(buf, strftime(buf,bufsize,"%F %T %Z",&t)); } std::string* eps=new std::string("%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: "); *eps+=ToString(bblx)+" "+ToString(bbly)+" "+ToString(bbrx)+" "+ToString(bbry); *eps+="\n%%Title: "+(title.empty()?std::string("untitled"):title); *eps+="\n%%Creator: gmt_makemap\n%%CreationDate: "+curtime; *eps+="\n%%LanguageLevel: 2\n%%Orientation: Portrait\n%%EndComments\n"; for(auto d: data) *eps+=*d; return new ObjectGMTMap(eps,bblx,bbly,bbrx,bbry); } fail: return 0; } // Creating pdf from eps /* Input: One argument must be GMTMap. Optionally, resolution can be specified by pair with name resolution, res or r and double value. Default is 720. */ ObjectBase* GMT_Convert2PDF(const ObjectList* input) { if(!gs_abilities.havepdf) return 0; // No pdf support double r; bool suc=true; const ObjectGMTMap* map=0; { SearchParameterWDefO,false,PMin<1> > res(input,"r","res","resolution"); r=res(&suc); if(!suc) goto fail; // Error } { for(ObjectList::ListValues::size_type i=0;iSize();i++) { OBType m(input->At(i)); if(m) { if(0!=map) goto fail; // Duplicate map=m; } } if(0==map) goto fail; // Map not found } { std::string* out=new std::string; int ret=eps2pdf(*(map->pValue()),out,r); if(0!=ret) { delete out; goto fail; } // Something wrong return new ObjectGMTMapPDF(out,map->Bblx(),map->Bbly(),map->Bbrx(),map->Bbry()); } fail: return 0; }