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.
224 lines
5.8 KiB
224 lines
5.8 KiB
9 years ago
|
#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;
|
||
|
}
|