You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
5.8 KiB
223 lines
5.8 KiB
#include <regex> |
|
#include <stack> |
|
#include "modgmt_map.h" |
|
#include "modgmt_func.h" |
|
#include "modgmt_gsfuncs.h" |
|
|
|
struct input_runtime |
|
{ |
|
std::list<std::shared_ptr<std::string> >::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<struct input_runtime*>(static_cast<struct gs_runtime*>(caller_handle)->indata); |
|
size_t buflen=static_cast<size_t>(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; |
|
} |
|
|
|
|
|
// 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<std::pair<const ObjectList*,decltype(pos)> > lstack; |
|
bool first=true; |
|
double xg,yg,x,y; |
|
bool xislocal=false, yislocal=false; |
|
std::list<std::shared_ptr<std::string> > 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<size) |
|
{ |
|
// Check if next argument is list |
|
{ |
|
OBType<ObjectList> 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<ObjectString> s(list->At(pos)); |
|
if(s) |
|
{ |
|
title=s->Value(); |
|
goto next; |
|
} |
|
} |
|
|
|
// Check if argument is pair |
|
{ |
|
OBType<ObjectPair> 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<ObjectGMTLayer> 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; |
|
}
|
|
|