23 #include "gvplugin_skillgui_cairo.h"
25 #include <utils/system/argparser.h>
30 #include <sys/types.h>
37 using namespace fawkes;
50 : argp(argc, argv,
"hi:o:f:wps:")
52 if (! (argp.has_arg(
"i") && argp.has_arg(
"o") && argp.has_arg(
"f"))
53 || argp.has_arg(
"h")) {
58 format = argp.arg(
"f");
61 white_bg = argp.has_arg(
"w");
62 postproc_required =
false;
63 do_postproc = argp.has_arg(
"p");
64 maxwidth = maxheight = 0;
67 if ( (format !=
"pdf") && (format !=
"svg") && (format !=
"png") ) {
68 printf(
"Unknown format '%s'\n\n", format.c_str());
73 if ( do_postproc && (format !=
"png") ) {
74 printf(
"Post-processing only available for PNG output format.\n");
78 if (argp.has_arg(
"s")) {
80 scale = strtod(argp.arg(
"s"), &endptr);
82 printf(
"Invalid scale value '%s', could not convert to number (failed at '%s').\n",
83 argp.arg(
"s"), endptr);
88 indir = argp.arg(
"i");
89 outdir = argp.arg(
"o");
91 struct stat statbuf_in, statbuf_out;
92 if (stat(indir.c_str(), &statbuf_in) != 0) {
93 perror(
"Unable to stat input directory");
96 if (stat(outdir.c_str(), &statbuf_out) != 0) {
97 perror(
"Unable to stat output directory");
100 if (! S_ISDIR(statbuf_in.st_mode) || ! S_ISDIR(statbuf_out.st_mode)) {
101 printf(
"Input or output directory is not a directory.\n\n");
105 char outdir_real[PATH_MAX];
106 if (realpath(outdir.c_str(), outdir_real)) {
107 outdir = outdir_real;
110 directory = opendir(indir.c_str());
112 printf(
"Could not open input directory\n");
117 gvplugin_skillgui_cairo_setup(gvc,
this);
130 printf(
"\nUsage: %s -i <dir> -o <dir> -f <format> [-w] [-s scale]\n"
131 " -i dir Input directory containing dot graphs\n"
132 " -o dir Output directory for generated graphs\n"
133 " -f format Output format, one of pdf, svg, or png\n"
134 " -w White background\n"
135 " -p Postprocess frames to same size (PNG only)\n"
136 " -s scale Scale factor to apply during rendering\n"
138 argp.program_name());
141 virtual Cairo::RefPtr<Cairo::Context> get_cairo()
146 if (format ==
"pdf") {
147 surface = Cairo::PdfSurface::create(outfile, bbw * scale, bbh * scale);
148 printf(
"Creating PDF context of size %f x %f\n", bbw * scale, bbh * scale);
149 }
else if (format ==
"svg") {
150 surface = Cairo::SvgSurface::create(outfile, bbw * scale, bbh * scale);
151 }
else if (format ==
"png") {
152 surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
153 (
int)ceilf(bbw * scale),
154 (
int)ceilf(bbh * scale));
157 cairo = Cairo::Context::create(surface);
159 cairo->set_source_rgb(1, 1, 1);
168 virtual void get_dimensions(
double &width,
double &height)
171 height = bbh * scale;
178 virtual void get_translation(
double &tx,
double &ty)
182 ty = (bbh - pad_y) * scale;
185 virtual void set_bb(
double bbw,
double bbh)
190 if ( bbw * scale > maxwidth ) {
191 postproc_required = (maxwidth != 0);
192 maxwidth = bbw * scale;
194 if ( bbh * scale > maxheight * scale ) {
195 postproc_required = (maxheight != 0);
196 maxheight = bbh * scale;
200 virtual void set_pad(
double pad_x,
double pad_y)
207 virtual void get_pad(
double &pad_x,
double &pad_y)
217 FILE *f = fopen(infile.c_str(),
"r");
218 Agraph_t *g = agread(f, NULL);
220 gvLayout(gvc, g, (
char *)
"dot");
221 gvRender(gvc, g, (
char *)
"skillguicairo", NULL);
222 gvFreeLayout(gvc, g);
228 surface->write_to_png(outfile);
240 while ((d = readdir(directory)) != NULL) {
241 if (fnmatch(
"*.dot", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
242 char infile_real[PATH_MAX];
243 infile = indir +
"/" + d->d_name;
244 if (realpath(infile.c_str(), infile_real)) {
245 infile = infile_real;
247 char *basefile = strdup(infile.c_str());
248 std::string basen = basename(basefile);
250 outfile = outdir +
"/" + basen.substr(0, basen.length() - 3) + format;
251 printf(
"Converting %s to %s\n", infile.c_str(), outfile.c_str());
254 printf(
"%s does not match pattern\n", d->d_name);
258 if (do_postproc && postproc_required) {
269 static cairo_status_t write_func(
void *closure,
270 const unsigned char *data,
unsigned int length)
272 FILE *f = (FILE *)closure;
273 if (fwrite(data, length, 1, f)) {
274 return CAIRO_STATUS_SUCCESS;
276 return CAIRO_STATUS_WRITE_ERROR;
283 printf(
"Post-processing PNG files, resizing to %fx%f\n", maxwidth, maxheight);
285 DIR *output_dir = opendir(outdir.c_str());
286 while ((d = readdir(output_dir)) != NULL) {
287 if (fnmatch(
"*.png", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
288 infile = outdir +
"/" + d->d_name;
289 Cairo::RefPtr<Cairo::ImageSurface> imgs = Cairo::ImageSurface::create_from_png(infile);
290 if ( (imgs->get_height() != maxheight) || (imgs->get_width() != maxwidth)) {
292 char *tmpout = strdup((outdir +
"/tmpXXXXXX").c_str());
293 FILE *f = fdopen(mkstemp(tmpout),
"w");
297 Cairo::RefPtr<Cairo::ImageSurface> outs = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
298 (
int)ceilf(maxwidth),
299 (
int)ceilf(maxheight));
300 double tx = (maxwidth - imgs->get_width()) / 2.0;
301 double ty = (maxheight - imgs->get_height()) / 2.0;
302 printf(
"Re-creating %s for post-processing, "
303 "resizing from %ix%i, tx=%f, ty=%f\n", infile.c_str(),
304 imgs->get_width(), imgs->get_height(), tx, ty);
305 Cairo::RefPtr<Cairo::Context> cc = Cairo::Context::create(outs);
307 cc->set_source_rgb(1, 1, 1);
310 cc->set_source(imgs, tx, ty);
317 rename(outfile.c_str(), infile.c_str());
321 closedir(output_dir);
328 Cairo::RefPtr<Cairo::Surface> surface;
329 Cairo::RefPtr<Cairo::Context> cairo;
339 double maxwidth, maxheight;
340 bool postproc_required;
348 main(
int argc,
char **argv)