2622c288 |
/* $Id: psusershape.c,v 1.34 2011/01/25 16:30:48 ellson Exp $ $Revision: 1.34 $ */
/* vim:set shiftwidth=4 ts=8: */
/*************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: See CVS logs. Details at http://www.graphviz.org/
*************************************************************************/
#ifndef WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include "render.h"
#include "gvio.h"
static int N_EPSF_files;
static Dict_t *EPSF_contents;
static void ps_image_free(Dict_t * dict, usershape_t * p, Dtdisc_t * disc)
{
free(p->data);
}
static Dtdisc_t ImageDictDisc = {
offsetof(usershape_t, name),/* key */
-1, /* size */
0, /* link offset */
NIL(Dtmake_f),
(Dtfree_f) ps_image_free,
NIL(Dtcompar_f),
NIL(Dthash_f),
NIL(Dtmemory_f),
NIL(Dtevent_f)
};
static usershape_t *user_init(const char *str)
{
char *contents;
char line[BUFSIZ];
FILE *fp;
struct stat statbuf;
int saw_bb, must_inline, rc;
int lx, ly, ux, uy;
usershape_t *us;
if (!EPSF_contents)
EPSF_contents = dtopen(&ImageDictDisc, Dtoset);
us = dtmatch(EPSF_contents, str);
if (us)
return us;
if (!(fp = fopen(str, "r"))) {
agerr(AGWARN, "couldn't open epsf file %s\n", str);
return NULL;
}
/* try to find size */
saw_bb = must_inline = FALSE;
while (fgets(line, sizeof(line), fp)) {
if (sscanf
(line, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) {
saw_bb = TRUE;
}
if ((line[0] != '%') && strstr(line,"read")) must_inline = TRUE;
if (saw_bb && must_inline) break;
}
if (saw_bb) {
us = GNEW(usershape_t);
us->x = lx;
us->y = ly;
us->w = ux - lx;
us->y = uy - ly;
us->name = str;
us->macro_id = N_EPSF_files++;
fstat(fileno(fp), &statbuf);
contents = us->data = N_GNEW(statbuf.st_size + 1, char);
fseek(fp, 0, SEEK_SET);
rc = fread(contents, statbuf.st_size, 1, fp);
contents[statbuf.st_size] = '\0';
dtinsert(EPSF_contents, us);
us->must_inline = must_inline;
} else {
agerr(AGWARN, "BoundingBox not found in epsf file %s\n", str);
us = NULL;
}
fclose(fp);
return us;
}
void epsf_init(node_t * n)
{
epsf_t *desc;
const char *str;
usershape_t *us;
int dx, dy;
if ((str = safefile(agget(n, "shapefile")))) {
us = user_init(str);
if (!us)
return;
dx = us->w;
dy = us->h;
ND_width(n) = PS2INCH(dx);
ND_height(n) = PS2INCH(dy);
ND_shape_info(n) = desc = NEW(epsf_t);
desc->macro_id = us->macro_id;
desc->offset.x = -us->x - (dx) / 2;
desc->offset.y = -us->y - (dy) / 2;
} else
agerr(AGWARN, "shapefile not set or not found for epsf node %s\n", agnameof(n));
}
void epsf_free(node_t * n)
{
if (ND_shape_info(n))
free(ND_shape_info(n));
}
/* cat_libfile:
* Write library files onto the given file pointer.
* arglib is an NULL-terminated array of char*
* Each non-trivial entry should be the name of a file to be included.
* stdlib is an NULL-terminated array of char*
* Each of these is a line of a standard library to be included.
* If any item in arglib is the empty string, the stdlib is not used.
* The stdlib is printed first, if used, followed by the user libraries.
* We check that for web-safe file usage.
*/
void cat_libfile(GVJ_t * job, const char **arglib, const char **stdlib)
{
FILE *fp;
const char **s, *bp, *p, *path;
int i;
boolean use_stdlib = TRUE;
/* check for empty string to turn off stdlib */
if (arglib) {
for (i = 0; use_stdlib && ((p = arglib[i])); i++) {
if (*p == '\0')
use_stdlib = FALSE;
}
}
if (use_stdlib)
for (s = stdlib; *s; s++) {
gvputs(job, *s);
gvputs(job, "\n");
}
if (arglib) {
for (i = 0; (p = arglib[i]) != 0; i++) {
if (*p == '\0')
continue; /* ignore empty string */
path = safefile(p); /* make sure filename is okay */
if (!path) {
agerr(AGWARN, "can't find library file %s\n", p);
}
else if ((fp = fopen(path, "r"))) {
while ((bp = Fgets(fp)))
gvputs(job, bp);
gvputs(job, "\n"); /* append a newline just in case */
fclose (fp);
} else
agerr(AGWARN, "can't open library file %s\n", path);
}
}
}
#define FILTER_EPSF 1
#ifdef FILTER_EPSF
/* this removes EPSF DSC comments that, when nested in another
* document, cause errors in Ghostview and other Postscript
* processors (although legal according to the Adobe EPSF spec).
*
* N.B. PostScript lines can end with \n, \r or \r\n.
*/
void epsf_emit_body(GVJ_t *job, usershape_t *us)
{
char *p;
char c;
p = us->data;
while (*p) {
/* skip %%EOF lines */
if ((p[0] == '%') && (p[1] == '%')
&& (!strncasecmp(&p[2], "EOF", 3)
|| !strncasecmp(&p[2], "BEGIN", 5)
|| !strncasecmp(&p[2], "END", 3)
|| !strncasecmp(&p[2], "TRAILER", 7)
)) {
/* check for *p since last line might not end in '\n' */
while ((c = *p) && (c != '\r') && (c != '\n')) p++;
if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
else if (*p) p++;
continue;
}
/* output line */
while ((c = *p) && (c != '\r') && (c != '\n')) {
gvputc(job, c);
p++;
}
if ((*p == '\r') && (*(p+1) == '\n')) p += 2;
else if (*p) p++;
gvputc(job, '\n');
}
}
#else
void epsf_emit_body(GVJ_t *job, usershape_t *us)
{
gvputs(job, us->data);
}
#endif
void epsf_define(GVJ_t *job)
{
usershape_t *us;
if (!EPSF_contents)
return;
for (us = dtfirst(EPSF_contents); us; us = dtnext(EPSF_contents, us)) {
if (us->must_inline)
continue;
gvprintf(job, "/user_shape_%d {\n", us->macro_id);
gvputs(job, "%%BeginDocument:\n");
epsf_emit_body(job, us);
gvputs(job, "%%EndDocument\n");
gvputs(job, "} bind def\n");
}
}
enum {ASCII, LATIN1, NONLATIN};
/* charsetOf:
* Assuming legal utf-8 input, determine if
* the character value range is ascii, latin-1 or otherwise.
*/
static int
charsetOf (char* s)
{
int r = ASCII;
unsigned char c;
while ((c = *(unsigned char*)s++)) {
if (c < 0x7F)
continue;
else if ((c & 0xFC) == 0xC0) {
r = LATIN1;
s++; /* eat second byte */
}
else return NONLATIN;
}
return r;
}
char *ps_string(char *ins, int latin)
{
char *s;
char *base;
static agxbuf xb;
static int warned;
int rc;
if (latin)
base = utf8ToLatin1 (ins);
else {
switch (charsetOf (ins)) {
case ASCII :
base = ins;
break;
case LATIN1 :
base = utf8ToLatin1 (ins);
break;
case NONLATIN :
if (!warned) {
agerr (AGWARN, "UTF-8 input uses non-Latin1 characters which cannot be handled by this PostScript driver\n");
warned = 1;
}
base = ins;
break;
default:
base = ins;
break;
}
}
if (xb.buf == NULL)
agxbinit (&xb, 0, NULL);
rc = agxbputc (&xb, LPAREN);
s = base;
while (*s) {
if ((*s == LPAREN) || (*s == RPAREN) || (*s == '\\'))
rc = agxbputc (&xb, '\\');
rc = agxbputc (&xb, *s++);
}
agxbputc (&xb, RPAREN);
if (base != ins) free (base);
s = agxbuse(&xb);
return s;
}
|