00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "gvplugin_skillgui_cairo.h"
00024
00025 #include <utils/system/argparser.h>
00026 #include <cstdlib>
00027 #include <cstring>
00028 #include <cstdio>
00029 #include <cmath>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <unistd.h>
00033 #include <dirent.h>
00034 #include <fnmatch.h>
00035 #include <libgen.h>
00036
00037 using namespace fawkes;
00038
00039
00040
00041 class SkillGuiBatchRenderer
00042 : public SkillGuiCairoRenderInstructor
00043 {
00044 public:
00045
00046
00047
00048
00049 SkillGuiBatchRenderer(int argc, char **argv)
00050 : argp(argc, argv, "hi:o:f:wps:")
00051 {
00052 if (! (argp.has_arg("i") && argp.has_arg("o") && argp.has_arg("f"))
00053 || argp.has_arg("h")) {
00054 usage();
00055 exit(-1);
00056 }
00057
00058 format = argp.arg("f");
00059 write_to_png = false;
00060 bbw = bbh = 0;
00061 white_bg = argp.has_arg("w");
00062 postproc_required = false;
00063 do_postproc = argp.has_arg("p");
00064 maxwidth = maxheight = 0;
00065 scale = 1.0;
00066
00067 if ( (format != "pdf") && (format != "svg") && (format != "png") ) {
00068 printf("Unknown format '%s'\n\n", format.c_str());
00069 usage();
00070 exit(-2);
00071 }
00072
00073 if ( do_postproc && (format != "png") ) {
00074 printf("Post-processing only available for PNG output format.\n");
00075 exit(-7);
00076 }
00077
00078 if (argp.has_arg("s")) {
00079 char *endptr;
00080 scale = strtod(argp.arg("s"), &endptr);
00081 if ( *endptr != 0 ) {
00082 printf("Invalid scale value '%s', could not convert to number (failed at '%s').\n",
00083 argp.arg("s"), endptr);
00084 exit(-8);
00085 }
00086 }
00087
00088 indir = argp.arg("i");
00089 outdir = argp.arg("o");
00090
00091 struct stat statbuf_in, statbuf_out;
00092 if (stat(indir.c_str(), &statbuf_in) != 0) {
00093 perror("Unable to stat input directory");
00094 exit(-3);
00095 }
00096 if (stat(outdir.c_str(), &statbuf_out) != 0) {
00097 perror("Unable to stat output directory");
00098 exit(-4);
00099 }
00100 if (! S_ISDIR(statbuf_in.st_mode) || ! S_ISDIR(statbuf_out.st_mode)) {
00101 printf("Input or output directory is not a directory.\n\n");
00102 exit(-5);
00103 }
00104
00105 char outdir_real[PATH_MAX];
00106 if (realpath(outdir.c_str(), outdir_real)) {
00107 outdir = outdir_real;
00108 }
00109
00110 directory = opendir(indir.c_str());
00111 if (! directory) {
00112 printf("Could not open input directory\n");
00113 exit(-6);
00114 }
00115
00116 gvc = gvContext();
00117 gvplugin_skillgui_cairo_setup(gvc, this);
00118 }
00119
00120
00121 ~SkillGuiBatchRenderer()
00122 {
00123 gvFreeContext(gvc);
00124 closedir(directory);
00125 }
00126
00127
00128 void usage()
00129 {
00130 printf("\nUsage: %s -i <dir> -o <dir> -f <format> [-w] [-s scale]\n"
00131 " -i dir Input directory containing dot graphs\n"
00132 " -o dir Output directory for generated graphs\n"
00133 " -f format Output format, one of pdf, svg, or png\n"
00134 " -w White background\n"
00135 " -p Postprocess frames to same size (PNG only)\n"
00136 " -s scale Scale factor to apply during rendering\n"
00137 "\n",
00138 argp.program_name());
00139 }
00140
00141 virtual Cairo::RefPtr<Cairo::Context> get_cairo()
00142 {
00143 if (! cairo) {
00144 this->bbw = bbw;
00145 this->bbh = bbh;
00146 if (format == "pdf") {
00147 surface = Cairo::PdfSurface::create(outfile, bbw * scale, bbh * scale);
00148 printf("Creating PDF context of size %f x %f\n", bbw * scale, bbh * scale);
00149 } else if (format == "svg") {
00150 surface = Cairo::SvgSurface::create(outfile, bbw * scale, bbh * scale);
00151 } else if (format == "png") {
00152 surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
00153 (int)ceilf(bbw * scale),
00154 (int)ceilf(bbh * scale));
00155 write_to_png = true;
00156 }
00157 cairo = Cairo::Context::create(surface);
00158 if (white_bg) {
00159 cairo->set_source_rgb(1, 1, 1);
00160 cairo->paint();
00161 }
00162 }
00163 return cairo;
00164 }
00165
00166 virtual bool scale_override() { return true; }
00167
00168 virtual void get_dimensions(double &width, double &height)
00169 {
00170 width = bbw * scale;
00171 height = bbh * scale;
00172 }
00173
00174 virtual double get_scale() { return scale; }
00175 virtual void set_scale(double scale) {};
00176 virtual void set_translation(double tx, double ty) {};
00177
00178 virtual void get_translation(double &tx, double &ty)
00179 {
00180
00181 tx = pad_x * scale;
00182 ty = (bbh - pad_y) * scale;
00183 }
00184
00185 virtual void set_bb(double bbw, double bbh)
00186 {
00187 this->bbw = bbw;
00188 this->bbh = bbh;
00189
00190 if ( bbw * scale > maxwidth ) {
00191 postproc_required = (maxwidth != 0);
00192 maxwidth = bbw * scale;
00193 }
00194 if ( bbh * scale > maxheight * scale ) {
00195 postproc_required = (maxheight != 0);
00196 maxheight = bbh * scale;
00197 }
00198 }
00199
00200 virtual void set_pad(double pad_x, double pad_y)
00201 {
00202 this->pad_x = pad_x;
00203 this->pad_y = pad_y;
00204 }
00205
00206
00207 virtual void get_pad(double &pad_x, double &pad_y)
00208 {
00209 pad_x = 0;
00210 pad_y = 0;
00211 }
00212
00213
00214 void render()
00215 {
00216
00217 FILE *f = fopen(infile.c_str(), "r");
00218 Agraph_t *g = agread(f);
00219 if (g) {
00220 gvLayout(gvc, g, (char *)"dot");
00221 gvRender(gvc, g, (char *)"skillguicairo", NULL);
00222 gvFreeLayout(gvc, g);
00223 agclose(g);
00224 }
00225 fclose(f);
00226
00227 if (write_to_png) {
00228 surface->write_to_png(outfile);
00229 }
00230
00231 cairo.clear();
00232 surface.clear();
00233 }
00234
00235
00236 void run()
00237 {
00238 struct dirent *d;
00239
00240 while ((d = readdir(directory)) != NULL) {
00241 if (fnmatch("*.dot", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
00242 char infile_real[PATH_MAX];
00243 infile = indir + "/" + d->d_name;
00244 if (realpath(infile.c_str(), infile_real)) {
00245 infile = infile_real;
00246 }
00247 char *basefile = strdup(infile.c_str());
00248 std::string basen = basename(basefile);
00249 free(basefile);
00250 outfile = outdir + "/" + basen.substr(0, basen.length() - 3) + format;
00251 printf("Converting %s to %s\n", infile.c_str(), outfile.c_str());
00252 render();
00253 } else {
00254 printf("%s does not match pattern\n", d->d_name);
00255 }
00256 }
00257
00258 if (do_postproc && postproc_required) {
00259 postprocess();
00260 }
00261 }
00262
00263
00264
00265
00266
00267
00268
00269 static cairo_status_t write_func(void *closure,
00270 const unsigned char *data, unsigned int length)
00271 {
00272 FILE *f = (FILE *)closure;
00273 if (fwrite(data, length, 1, f)) {
00274 return CAIRO_STATUS_SUCCESS;
00275 } else {
00276 return CAIRO_STATUS_WRITE_ERROR;
00277 }
00278 }
00279
00280
00281 void postprocess()
00282 {
00283 printf("Post-processing PNG files, resizing to %fx%f\n", maxwidth, maxheight);
00284 struct dirent *d;
00285 DIR *output_dir = opendir(outdir.c_str());
00286 while ((d = readdir(output_dir)) != NULL) {
00287 if (fnmatch("*.png", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
00288 infile = outdir + "/" + d->d_name;
00289 Cairo::RefPtr<Cairo::ImageSurface> imgs = Cairo::ImageSurface::create_from_png(infile);
00290 if ( (imgs->get_height() != maxheight) || (imgs->get_width() != maxwidth)) {
00291
00292 char *tmpout = strdup((outdir + "/tmpXXXXXX").c_str());
00293 FILE *f = fdopen(mkstemp(tmpout), "w");
00294 outfile = tmpout;
00295 free(tmpout);
00296
00297 Cairo::RefPtr<Cairo::ImageSurface> outs = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
00298 (int)ceilf(maxwidth),
00299 (int)ceilf(maxheight));
00300 double tx = (maxwidth - imgs->get_width()) / 2.0;
00301 double ty = (maxheight - imgs->get_height()) / 2.0;
00302 printf("Re-creating %s for post-processing, "
00303 "resizing from %ix%i, tx=%f, ty=%f\n", infile.c_str(),
00304 imgs->get_width(), imgs->get_height(), tx, ty);
00305 Cairo::RefPtr<Cairo::Context> cc = Cairo::Context::create(outs);
00306 if (white_bg) {
00307 cc->set_source_rgb(1, 1, 1);
00308 cc->paint();
00309 }
00310 cc->set_source(imgs, tx, ty);
00311 cc->paint();
00312 outs->write_to_png(&SkillGuiBatchRenderer::write_func, f);
00313 imgs.clear();
00314 cc.clear();
00315 outs.clear();
00316 fclose(f);
00317 rename(outfile.c_str(), infile.c_str());
00318 }
00319 }
00320 }
00321 closedir(output_dir);
00322 }
00323
00324 private:
00325 GVC_t *gvc;
00326 ArgumentParser argp;
00327 std::string format;
00328 Cairo::RefPtr<Cairo::Surface> surface;
00329 Cairo::RefPtr<Cairo::Context> cairo;
00330 bool write_to_png;
00331 bool white_bg;
00332 double bbw, bbh;
00333 double pad_x, pad_y;
00334 std::string infile;
00335 std::string outfile;
00336 std::string indir;
00337 std::string outdir;
00338 DIR *directory;
00339 double maxwidth, maxheight;
00340 bool postproc_required;
00341 bool do_postproc;
00342 double scale;
00343 };
00344
00345
00346
00347 int
00348 main(int argc, char **argv)
00349 {
00350 SkillGuiBatchRenderer renderer(argc, argv);
00351 renderer.run();
00352 return 0;
00353 }