-static int eps_add_font(eps_state *state, gfx_node_t *node)
-{
- /* The fonts list could be postponed to the end using
- (atend), but let's be nice and have them in the header. */
- const char *ps_font = afm_get_font_postscript_name(node->filename);
- eps_font *ef;
- for (ef = state->font_list; ef; ef = ef->next) {
- if (!strcmp(ps_font, ef->ps_font))
- return 0;
- }
- ef = malloc(sizeof(eps_font));
- if (ef == NULL) {
- rrd_set_error("malloc for eps_font");
- return -1;
- }
- ef->next = state->font_list;
- ef->ps_font = ps_font;
- state->font_list = ef;
- return 0;
-}
-
-static void eps_list_fonts(eps_state *state, const char *dscName)
-{
- eps_font *ef;
- int lineLen = strlen(dscName);
- if (!state->font_list)
- return;
- fputs(dscName, state->fp);
- for (ef = state->font_list; ef; ef = ef->next) {
- int nameLen = strlen(ef->ps_font);
- if (lineLen + nameLen > 100 && lineLen) {
- fputs("\n", state->fp);
- fputs("%%- \n", state->fp);
- lineLen = 5;
- } else {
- fputs(" ", state->fp);
- lineLen++;
- }
- fputs(ef->ps_font, state->fp);
- lineLen += nameLen;
- }
- fputs("\n", state->fp);
-}
-
-static void eps_define_fonts(eps_state *state)
-{
- eps_font *ef;
- if (!state->font_list)
- return;
- for (ef = state->font_list; ef; ef = ef->next) {
- /* PostScript¨ LANGUAGE REFERENCE third edition
- page 349 */
- fprintf(state->fp,
- "%%\n"
- "/%s findfont dup length dict begin\n"
- "{ 1 index /FID ne {def} {pop pop} ifelse } forall\n"
- "/Encoding ISOLatin1Encoding def\n"
- "currentdict end\n"
- "/%s-ISOLatin1 exch definefont pop\n"
- "/SetFont-%s { /%s-ISOLatin1 findfont exch scalefont setfont } bd\n",
- ef->ps_font, ef->ps_font, ef->ps_font, ef->ps_font);
- }
-}
-
-static int eps_prologue(eps_state *state)
-{
- gfx_node_t *node;
- fputs(
- "%!PS-Adobe-3.0 EPSF-3.0\n"
- "%%Creator: RRDtool 1.2rc4 Tobias Oetiker, http://tobi.oetiker.ch\n"
- /* can't like weird chars here */
- "%%Title: (RRDtool output)\n"
- "%%DocumentData: Clean7Bit\n"
- "", state->fp);
- fprintf(state->fp, "%%%%BoundingBox: 0 0 %d %d\n",
- state->page_width, state->page_height);
- for (node = state->canvas->firstnode; node; node = node->next) {
- if (node->type == GFX_TEXT && eps_add_font(state, node) == -1)
- return -1;
- }
- eps_list_fonts(state, "%%DocumentFonts:");
- eps_list_fonts(state, "%%DocumentNeededFonts:");
- fputs(
- "%%EndComments\n"
- "%%BeginProlog\n"
- "%%EndProlog\n" /* must have, or BoundingBox is ignored */
- "/bd { bind def } bind def\n"
- "", state->fp);
- fprintf(state->fp, "/X { %.2f add } bd\n", LINEOFFSET);
- fputs(
- "/X2 {X exch X exch} bd\n"
- "/M {X2 moveto} bd\n"
- "/L {X2 lineto} bd\n"
- "/m {moveto} bd\n"
- "/l {lineto} bd\n"
- "/S {stroke} bd\n"
- "/CP {closepath} bd\n"
- "/WS {setlinewidth stroke} bd\n"
- "/F {fill} bd\n"
- "/TaL { } bd\n"
- "/TaC {dup stringwidth pop neg 2 div 0 rmoveto } bd\n"
- "/TaR {dup stringwidth pop neg 0 rmoveto } bd\n"
- "/TL {moveto TaL show} bd\n"
- "/TC {moveto TaC show} bd\n"
- "/TR {moveto TaR show} bd\n"
- "/Rgb { 255.0 div 3 1 roll\n"
- " 255.0 div 3 1 roll \n"
- " 255.0 div 3 1 roll setrgbcolor } bd\n"
- "", state->fp);
- eps_define_fonts(state);
- return 0;
-}
-
-static void eps_clear_dash(eps_state *state)
-{
- if (!state->has_dash)
- return;
- state->has_dash = 0;
- fputs("[1 0] 0 setdash\n", state->fp);
-}
-
-static void eps_write_linearea(eps_state *state, gfx_node_t *node)
-{
- int i;
- FILE *fp = state->fp;
- int useOffset = 0;
- int clearDashIfAny = 1;
- eps_set_color(state, node->color);
- if (node->type == GFX_LINE) {
- svg_dash dash_info;
- if (state->linecap != 1) {
- fputs("1 setlinecap\n", fp);
- state->linecap = 1;
- }
- if (state->linejoin != 1) {
- fputs("1 setlinejoin\n", fp);
- state->linejoin = 1;
- }
- svg_get_dash(node, &dash_info);
- if (dash_info.dash_enable) {
- clearDashIfAny = 0;
- state->has_dash = 1;
- fputs("[", fp);
- svg_write_number(fp, dash_info.adjusted_on);
- fputs(" ", fp);
- svg_write_number(fp, dash_info.adjusted_off);
- fputs("] ", fp);
- svg_write_number(fp, dash_info.dash_offset);
- fputs(" setdash\n", fp);
- }
- }
- if (clearDashIfAny)
- eps_clear_dash(state);
- for (i = 0; i < node->points; i++) {
- ArtVpath *vec = node->path + i;
- double x = vec->x;
- double y = state->page_height - vec->y;
- if (vec->code == ART_MOVETO_OPEN || vec->code == ART_MOVETO)
- useOffset = (fabs(x - floor(x) - 0.5) < 0.01 && fabs(y - floor(y) - 0.5) < 0.01);
- if (useOffset) {
- x -= LINEOFFSET;
- y -= LINEOFFSET;
- }
- switch (vec->code) {
- case ART_MOVETO_OPEN: /* fall-through */
- case ART_MOVETO:
- svg_write_number(fp, x);
- fputc(' ', fp);
- svg_write_number(fp, y);
- fputc(' ', fp);
- fputs(useOffset ? "M\n" : "m\n", fp);
- break;
- case ART_LINETO:
- svg_write_number(fp, x);
- fputc(' ', fp);
- svg_write_number(fp, y);
- fputc(' ', fp);
- fputs(useOffset ? "L\n" : "l\n", fp);
- break;
- case ART_CURVETO: break; /* unsupported */
- case ART_END: break; /* nop */
- }
- }
- if (node->type == GFX_LINE) {
- if (node->closed_path)
- fputs("CP ", fp);
- if (node->size != state->line_width) {
- state->line_width = node->size;
- svg_write_number(fp, state->line_width);
- fputs(" WS\n", fp);
- } else {
- fputs("S\n", fp);
- }
- } else {
- fputs("F\n", fp);
- }
-}
-
-static void eps_write_text(eps_state *state, gfx_node_t *node)
-{
- FILE *fp = state->fp;
- const unsigned char *p;
- const char *ps_font = afm_get_font_postscript_name(node->filename);
- char align = 'L';
- double x = node->x;
- double y = state->page_height - node->y, ydelta = 0;
- int lineLen = 0;
- eps_set_color(state, node->color);
- if (strcmp(ps_font, state->font) || node->size != state->font_size) {
- state->font = ps_font;
- state->font_size = node->size;
- svg_write_number(fp, state->font_size);
- fprintf(fp, " SetFont-%s\n", state->font);
- }
- fputs("(", fp);
- lineLen = 20;
- for (p = (const unsigned char*)node->text; *p; p++) {
- if (lineLen > 70) {
- fputs("\\\n", fp); /* backslash and \n */
- lineLen = 0;
- }
- switch (*p) {
- case '(':
- case ')':
- case '\\':
- case '\n':
- case '\r':
- case '\t':
- fputc('\\', fp);
- lineLen++;
- /* fall-through */
- default:
- if (*p >= 126)
- fprintf(fp, "\\%03o", *p);
- else
- fputc(*p, fp);
- lineLen++;
- }
- }
- fputs(") ", fp);
- switch(node->valign){
- case GFX_V_TOP: ydelta = -node->size; break;
- case GFX_V_CENTER: ydelta = -node->size / 3.0; break;
- case GFX_V_BOTTOM: break;
- case GFX_V_NULL: break;
- }
- if (node->angle == 0)
- y += ydelta;
- switch (node->halign) {
- case GFX_H_RIGHT: align = 'R'; break;
- case GFX_H_CENTER: align = 'C'; break;
- case GFX_H_LEFT: align= 'L'; break;
- case GFX_H_NULL: align= 'L'; break;
- }
- if (node->angle != 0) {
- fputs("\n", fp);
- fputs(" gsave ", fp);
- svg_write_number(fp, x);
- fputc(' ', fp);
- svg_write_number(fp, y);
- fputs(" translate ", fp);
- svg_write_number(fp, -node->angle);
- fputs(" rotate 0 ", fp);
- svg_write_number(fp, ydelta);
- fputs(" moveto ", fp);
- fprintf(fp, "Ta%c", align);
- fputs(" show grestore\n", fp);
- } else {
- svg_write_number(fp, x);
- fputc(' ', fp);
- svg_write_number(fp, y);
- fputs(" T", fp);
- fputc(align, fp);
- fputc('\n', fp);
- }
-}
-
-static int eps_write_content(eps_state *state)
-{
- gfx_node_t *node;
- fputs("%\n", state->fp);
- for (node = state->canvas->firstnode; node; node = node->next) {
- switch (node->type) {
- case GFX_LINE:
- case GFX_AREA:
- eps_write_linearea(state, node);
- break;
- case GFX_TEXT:
- eps_write_text(state, node);
- break;
- }
- }
- return 0;
-}
-
-int gfx_render_eps (gfx_canvas_t *canvas,
- art_u32 width, art_u32 height,
- gfx_color_t background, FILE *fp){
- struct eps_state state;
- state.fp = fp;
- state.canvas = canvas;
- state.page_width = width;
- state.page_height = height;
- state.font = "no-default-font";
- state.font_size = -1;
- state.color = 0; /* black */
- state.font_list = NULL;
- state.linecap = -1;
- state.linejoin = -1;
- state.has_dash = 0;
- if (eps_prologue(&state) == -1)
- return -1;
- eps_set_color(&state, background);
- fprintf(fp, "0 0 M 0 %d L %d %d L %d 0 L fill\n",
- height, width, height, width);
- if (eps_write_content(&state) == -1)
- return 0;
- fputs("showpage\n", fp);
- fputs("%%EOF\n", fp);
- while (state.font_list) {
- eps_font *next = state.font_list->next;
- free(state.font_list);
- state.font_list = next;
- }
- return 0;
-}
-
-/* ------- PDF -------
- PDF references page:
- http://partners.adobe.com/asn/developer/technotes/acrobatpdf.html
-*/
-
-typedef struct pdf_buffer
-{
- int id, is_obj, is_dict, is_stream, pdf_file_pos;
- char *data;
- int alloc_size, current_size;
- struct pdf_buffer *previous_buffer, *next_buffer;
- struct pdf_state *state;
-} pdf_buffer;
-
-typedef struct pdf_font
-{
- const char *ps_font;
- pdf_buffer obj;
- struct pdf_font *next;
-} pdf_font;
-
-typedef struct pdf_state
-{
- FILE *fp;
- gfx_canvas_t *canvas;
- art_u32 page_width, page_height;
- pdf_font *font_list;
- pdf_buffer *first_buffer, *last_buffer;
- int pdf_file_pos;
- int has_failed;
- /*--*/
- gfx_color_t stroke_color, fill_color;
- int font_id;
- double font_size;
- double line_width;
- svg_dash dash;
- int linecap, linejoin;
- int last_obj_id;
- /*--*/
- pdf_buffer pdf_header;
- pdf_buffer catalog_obj, pages_obj, page1_obj;
- pdf_buffer fontsdict_obj;
- pdf_buffer graph_stream;
-} pdf_state;
-
-static void pdf_init_buffer(pdf_state *state, pdf_buffer *buf)
-{
- int initial_size = 32;
- buf->state = state;
- buf->id = -42;
- buf->alloc_size = 0;
- buf->current_size = 0;
- buf->data = (char*)malloc(initial_size);
- buf->is_obj = 0;
- buf->previous_buffer = NULL;
- buf->next_buffer = NULL;
- if (buf->data == NULL) {
- rrd_set_error("malloc for pdf_buffer data");
- state->has_failed = 1;
- return;
- }
- buf->alloc_size = initial_size;
- if (state->last_buffer)
- state->last_buffer->next_buffer = buf;
- if (state->first_buffer == NULL)
- state->first_buffer = buf;
- buf->previous_buffer = state->last_buffer;
- state->last_buffer = buf;
-}
-
-static void pdf_put(pdf_buffer *buf, const char *text, int len)
-{
- if (len <= 0)
- return;
- if (buf->alloc_size < buf->current_size + len) {
- int new_size = buf->alloc_size;
- char *new_buf;
- while (new_size < buf->current_size + len)
- new_size *= 4;
- new_buf = (char*)malloc(new_size);
- if (new_buf == NULL) {
- rrd_set_error("re-malloc for pdf_buffer data");
- buf->state->has_failed = 1;
- return;
- }
- memcpy(new_buf, buf->data, buf->current_size);
- free(buf->data);
- buf->data = new_buf;
- buf->alloc_size = new_size;
- }
- memcpy(buf->data + buf->current_size, text, len);
- buf->current_size += len;
-}
-
-static void pdf_puts(pdf_buffer *buf, const char *text)
-{
- pdf_put(buf, text, strlen(text));
-}
-
-static void pdf_indent(pdf_buffer *buf)
-{
- pdf_puts(buf, "\t");
-}
-
-static void pdf_putsi(pdf_buffer *buf, const char *text)
-{
- pdf_indent(buf);
- pdf_puts(buf, text);
-}
-
-static void pdf_putint(pdf_buffer *buf, int i)
-{
- char tmp[20];
- sprintf(tmp, "%d", i);
- pdf_puts(buf, tmp);
-}