123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include "config.h"
- #include <assert.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <sys/stat.h>
- #include <gvc/gvplugin_loadimage.h>
- #ifdef HAVE_GS
- #ifdef HAVE_PANGOCAIRO
- #include <ghostscript/iapi.h>
- #include <ghostscript/ierrors.h>
- #include <cairo/cairo.h>
- /**
- * Ensure compatibility with Ghostscipt versions newer than 9.18
- * while maintaining compatibility with the older versions.
- **/
- #ifndef e_VMerror
- #define e_VMerror gs_error_VMerror
- #endif
- #ifndef e_unregistered
- #define e_unregistered gs_error_unregistered
- #endif
- #ifndef e_invalidid
- #define e_invalidid gs_error_invalidid
- #endif
- typedef enum {
- FORMAT_PS_CAIRO, FORMAT_EPS_CAIRO,
- } format_type;
- typedef struct gs_s {
- cairo_t* cr;
- cairo_surface_t* surface;
- cairo_pattern_t* pattern;
- } gs_t;
- static void gvloadimage_gs_free(usershape_t *us)
- {
- gs_t *gs = us->data;
- if (gs->pattern) cairo_pattern_destroy(gs->pattern);
- if (gs->surface) cairo_surface_destroy(gs->surface);
- free(gs);
- }
- static int gs_writer(void *caller_handle, const char *str, int len)
- {
- GVJ_t *job = caller_handle;
- if (job->common->verbose) {
- assert(len >= 0);
- return (int)fwrite(str, 1, (size_t)len, stderr);
- }
- return len;
- }
- static void gs_error(GVJ_t * job, const char *name, const char *funstr, int err)
- {
- const char *errsrc;
- assert (err < 0);
- if (err >= e_VMerror)
- errsrc = "PostScript Level 1";
- else if (err >= e_unregistered)
- errsrc = "PostScript Level 2";
- else if (err >= e_invalidid)
- errsrc = "DPS error";
- else
- errsrc = "Ghostscript internal error";
- job->common->errorfn("%s: %s() returned: %d (%s)\n",
- name, funstr, err, errsrc);
- }
- static int gvloadimage_process_file(GVJ_t *job, usershape_t *us, void *instance)
- {
- int rc = 0, exit_code;
- if (! gvusershape_file_access(us)) {
- job->common->errorfn("Failure to read shape file\n");
- return -1;
- }
- rc = gsapi_run_file(instance, us->name, -1, &exit_code);
- if (rc) {
- gs_error(job, us->name, "gsapi_run_file", rc);
- }
- gvusershape_file_release(us);
- return rc;
- }
- static int gvloadimage_process_surface(GVJ_t *job, usershape_t *us, gs_t *gs, void *instance)
- {
- cairo_t *cr; /* temp cr for gs */
- int rc, rc2;
- char width_height[20], dpi[10], cairo_context[30];
- char *gs_args[] = {
- "dot", /* actual value of argv[0] doesn't matter */
- "-dQUIET",
- "-dNOPAUSE",
- "-sDEVICE=cairo",
- cairo_context,
- width_height,
- dpi,
- };
- #define GS_ARGC sizeof(gs_args)/sizeof(gs_args[0])
- gs->surface = cairo_surface_create_similar(
- cairo_get_target(gs->cr),
- CAIRO_CONTENT_COLOR_ALPHA,
- (int)(us->x + us->w),
- (int)(us->y + us->h));
- cr = cairo_create(gs->surface); /* temp context for gs */
- snprintf(width_height, sizeof(width_height), "-g%0.fx%0.f", us->x + us->w,
- us->y + us->h);
- snprintf(dpi, sizeof(dpi), "-r%d", us->dpi);
- snprintf(cairo_context, sizeof(cairo_context), "-sCairoContext=%p", cr);
- rc = gsapi_init_with_args(instance, GS_ARGC, gs_args);
- cairo_destroy(cr); /* finished with temp context */
- if (rc)
- gs_error(job, us->name, "gsapi_init_with_args", rc);
- else
- rc = gvloadimage_process_file(job, us, instance);
- if (rc) {
- cairo_surface_destroy(gs->surface);
- gs->surface = NULL;
- }
- rc2 = gsapi_exit(instance);
- if (rc2) {
- gs_error(job, us->name, "gsapi_exit", rc2);
- return rc2;
- }
- if (!rc)
- gs->pattern = cairo_pattern_create_for_surface (gs->surface);
- return rc;
- }
- static cairo_pattern_t* gvloadimage_gs_load(GVJ_t * job, usershape_t *us)
- {
- gs_t *gs = NULL;
- gsapi_revision_t gsapi_revision_info;
- void *instance;
- int rc;
- assert(job);
- assert(us);
- assert(us->name);
- if (us->data) {
- if (us->datafree == gvloadimage_gs_free
- && ((gs_t*)(us->data))->cr == job->context)
- gs = us->data; /* use cached data */
- else {
- us->datafree(us); /* free incompatible cache data */
- us->data = NULL;
- }
- }
- if (!gs) {
- gs = malloc(sizeof(gs_t));
- if (!gs) {
- job->common->errorfn("malloc() failure\n");
- return NULL;
- }
- gs->cr = job->context;
- gs->surface = NULL;
- gs->pattern = NULL;
- /* cache this - even if things go bad below - avoids repeats */
- us->data = gs;
- us->datafree = gvloadimage_gs_free;
- #define GSAPI_REVISION_REQUIRED 863
- rc = gsapi_revision(&gsapi_revision_info, sizeof(gsapi_revision_t));
- if (rc && rc < (int)sizeof(gsapi_revision_t)) {
- job->common->errorfn("gs revision - struct too short %d\n", rc);
- return NULL;
- }
- if (gsapi_revision_info.revision < GSAPI_REVISION_REQUIRED) {
- job->common->errorfn("gs revision - too old %d\n",
- gsapi_revision_info.revision);
- return NULL;
- }
- rc = gsapi_new_instance(&instance, job);
- if (rc)
- gs_error(job, us->name, "gsapi_new_instance", rc);
- else {
- rc = gsapi_set_stdio(instance, NULL, gs_writer, gs_writer);
- if (rc)
- gs_error(job, us->name, "gsapi_set_stdio", rc);
- else
- rc = gvloadimage_process_surface(job, us, gs, instance);
- gsapi_delete_instance(instance);
- }
- }
- return gs->pattern;
- }
- static void gvloadimage_gs_cairo(GVJ_t * job, usershape_t *us, boxf b, bool filled)
- {
- (void)filled;
- cairo_t *cr = job->context; // target context
- cairo_pattern_t *pattern = gvloadimage_gs_load(job, us);
- if (pattern) {
- cairo_save(cr);
- cairo_translate(cr, b.LL.x - us->x, -b.UR.y);
- cairo_scale(cr, (b.UR.x - b.LL.x) / us->w, (b.UR.y - b.LL.y) / us->h);
- cairo_set_source(cr, pattern);
- cairo_paint(cr);
- cairo_restore(cr);
- }
- }
- static gvloadimage_engine_t engine_cairo = {
- gvloadimage_gs_cairo
- };
- #endif
- #endif
- gvplugin_installed_t gvloadimage_gs_types[] = {
- #ifdef HAVE_GS
- #ifdef HAVE_PANGOCAIRO
- {FORMAT_PS_CAIRO, "ps:cairo", 1, &engine_cairo, NULL},
- {FORMAT_EPS_CAIRO, "eps:cairo", 1, &engine_cairo, NULL},
- #endif
- #endif
- {0, NULL, 0, NULL, NULL}
- };
|