From 8802277bdbe2a237f29a56079227f78bca315f19 Mon Sep 17 00:00:00 2001 From: Michael Uleysky Date: Fri, 12 Feb 2016 22:01:42 +1000 Subject: [PATCH] Gmt module: Add interface to ghostscript. --- modules/gmt/modgmt.cpp | 6 ++ modules/gmt/modgmt_gsfuncs.cpp | 165 +++++++++++++++++++++++++++++++++ modules/gmt/modgmt_gsfuncs.h | 38 ++++++++ 3 files changed, 209 insertions(+) create mode 100644 modules/gmt/modgmt_gsfuncs.cpp create mode 100644 modules/gmt/modgmt_gsfuncs.h diff --git a/modules/gmt/modgmt.cpp b/modules/gmt/modgmt.cpp index 59c75a0..93be950 100644 --- a/modules/gmt/modgmt.cpp +++ b/modules/gmt/modgmt.cpp @@ -1,6 +1,7 @@ #include "modgmt.h" #include "modgmt_func.h" #include "modgmt_internals.h" +#include "modgmt_gsfuncs.h" // Initialisation function int gmt_module_init(void* p) @@ -42,5 +43,10 @@ int gmt_module_init(void* p) RegisterFunction("Font",GMT_Type); RegisterFunction("Shift",GMT_LayerShift); RegisterFunction("DrawFrame",GMT_DrawFrame); + + CheckGhostscriptAbilities(); + // Calculating bounding box is critical + if(!gs_abilities.havebbox) return 1; + return 0; } diff --git a/modules/gmt/modgmt_gsfuncs.cpp b/modules/gmt/modgmt_gsfuncs.cpp new file mode 100644 index 0000000..cd2d646 --- /dev/null +++ b/modules/gmt/modgmt_gsfuncs.cpp @@ -0,0 +1,165 @@ +#include +#include +#include "common.h" +#include "modgmt_gsfuncs.h" + +struct gs_abilities_struct gs_abilities; + +static int gs_callback_out(void *caller_handle, const char *buf, int len) +{ + struct gs_runtime* r=static_cast(caller_handle); + if(0!=r->out) r->out->append(buf,len); + return len; +} + +static int gs_callback_err(void *caller_handle, const char *buf, int len) +{ + struct gs_runtime* r=static_cast(caller_handle); + if(0!=r->err) r->err->append(buf,len); + return len; +} + +static int gs_callback_in(void *caller_handle, char *buf, int len) +{ + struct gs_runtime* r=static_cast(caller_handle); + if(r->pos>=r->in->length()) return 0; + size_t rem=r->in->length()-r->pos; + if(rem>static_cast(len)) rem=len; + memcpy(buf,r->in->c_str()+r->pos,rem); + r->pos+=rem; + return rem; +} + +struct gsworkthreadpars +{ + struct gs_runtime* r; + gs_callback input_callback; + int fd; + const std::string* opts; + int ret; +}; + +// This function call gs_* in separate thread, because ghostscript write to stdout +static void* gsworkthread(void* x) +{ + struct gsworkthreadpars* p=reinterpret_cast(x); + WordList wl; + if(-1==p->fd) wl=Split("-dSAFER -q -o/dev/null "+*(p->opts)+" -"); + else wl=Split("-dSAFER -dDOINTERPOLATE -q -o/dev/fd/"+ToString(p->fd)+" "+*(p->opts)+" -"); + int argc=wl.size()+1; + char** argv; + int i; + void* gs; + + argv=new char*[argc]; + argv[0]=new char[2]; argv[0][0]='G'; argv[0][1]=0; + i=1; + for(auto& opt: wl) + { + argv[i]=new char[opt.length()+1]; + memcpy(argv[i],opt.c_str(),opt.length()+1); + i++; + } + + p->ret=gsapi_new_instance(&gs,p->r); + if(p->ret<0) goto end; + gsapi_set_stdio(gs,p->input_callback?p->input_callback:gs_callback_in,gs_callback_out,gs_callback_err); + p->ret=gsapi_init_with_args(gs,argc,argv); + if(0==p->ret || e_Quit==p->ret) p->ret=gsapi_exit(gs); + else gsapi_exit(gs); + if(e_Quit==p->ret) p->ret=0; + gsapi_delete_instance(gs); + + end: + close(p->fd); + for(i=0;iret; +} + +// Main function for prepare and run ghostscript +static int GhostRun(const std::string& opts, struct gs_runtime* r, std::string* out, gs_callback input=0) +{ + struct gsworkthreadpars wp; + int* pret; + + wp.r=r; + wp.input_callback=input; + wp.opts=&opts; + wp.ret=0; + wp.fd=-1; + + if(0!=out) + { + int pipefd[2]; + pthread_t wthr; + ssize_t br; + char buffer[4096]; + + pipe(pipefd); + wp.fd=pipefd[1]; + + out->erase(); + pthread_create(&wthr,0,&gsworkthread,&wp); + do + { + br=read(pipefd[0],buffer,4096); + out->append(buffer,br); + } while(0!=br); + close(pipefd[0]); + out->shrink_to_fit(); + pthread_join(wthr,reinterpret_cast(&pret)); + } + else pret=reinterpret_cast(gsworkthread(&wp)); + + return *pret; +} + + +// This function run ghostscript with input data in string +int GhostRun(const std::string& opts, const std::string& input, std::string* sout, std::string* serr, std::string* out) +{ + struct gs_runtime r; + + r.in=&input; + r.pos=0; + r.out=sout; + r.err=serr; + return GhostRun(opts,&r,out); +} + +// This function run ghostscript with input data provided by callback function +int GhostRun(const std::string& opts, gs_callback input, void* inputdata, std::string* sout, std::string* serr, std::string* out) +{ + struct gs_runtime r; + + r.indata=inputdata; + r.out=sout; + r.err=serr; + return GhostRun(opts,&r,out,input); +} + +void CheckGhostscriptAbilities() +{ + std::string out; + gs_abilities.havepdf=gs_abilities.havebbox=gs_abilities.havepngmono=gs_abilities.havepngmonod=gs_abilities.havepng16=gs_abilities.havepng256=gs_abilities.havepnggray=gs_abilities.havepng16m=gs_abilities.havejpeg=gs_abilities.havejpeggray=false; + + if(0!=GhostRun("-sDEVICE=nullpage","devicenames ==",&out,0,0)) return; + + WordList wl=Split(out,"[]/ \t"); + for(auto& i:wl) + { + if("pdfwrite"==i) gs_abilities.havepdf=true; + if("bbox"==i) gs_abilities.havebbox=true; + if("pngmono"==i) gs_abilities.havepngmono=true; + if("pngmonod"==i) gs_abilities.havepngmonod=true; + if("png16"==i) gs_abilities.havepng16=true; + if("png256"==i) gs_abilities.havepng256=true; + if("pnggray"==i) gs_abilities.havepnggray=true; + if("png16m"==i) gs_abilities.havepng16m=true; + if("jpeg"==i) gs_abilities.havejpeg=true; + if("jpeggray"==i) gs_abilities.havejpeggray=true; + } + +// gs_abilities.havepdf=(0==GhostRun("-sDEVICE=pdfwrite -dCompatibilityLevel=1.4","gsave 1 0 0 setrgbcolor 5 setlinewidth 100 100 moveto 200 200 lineto stroke grestore showpage",0,0,0)); +} diff --git a/modules/gmt/modgmt_gsfuncs.h b/modules/gmt/modgmt_gsfuncs.h new file mode 100644 index 0000000..412f20f --- /dev/null +++ b/modules/gmt/modgmt_gsfuncs.h @@ -0,0 +1,38 @@ +#ifndef MODGMT_GSFUNCS_H +#define MODGMT_GSFUNCS_H +#include +#include +#include + +struct gs_abilities_struct +{ + bool havepngmono,havepngmonod,havepng16,havepng256,havepnggray,havepng16m; + bool havejpeg,havejpeggray; + bool havepdf,havebbox; +}; + +extern struct gs_abilities_struct gs_abilities; + +struct gs_runtime +{ + union + { + struct + { + const std::string* in; + uint pos; + }; + void* indata; + }; + std::string* out; + std::string* err; +}; + +typedef int (*gs_callback)(void *caller_handle, char *buf, int len); + +int GhostRun(const std::string& opts, const std::string& input, std::string* sout, std::string* serr, std::string* out); +int GhostRun(const std::string& opts, gs_callback input, void* inputdata, std::string* sout, std::string* serr, std::string* out); + +void CheckGhostscriptAbilities(); + +#endif