/*
 * Maketool - GTK-based front end for gmake
 * Copyright (c) 1999-2003 Greg Banks
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "maketool.h"
#include "ps.h"
#include "util.h"
#include <time.h>

CVSID("$Id: ps.c,v 1.9 2003/08/10 10:19:39 gnb Exp $");

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

typedef struct
{
    gboolean defined;
    float red, green, blue;
} PsColor;

typedef struct
{
    PsColor foreground, background;
} PsStyle;

struct _PsDocument
{
    FILE *fp;
    gboolean in_page;
    char *title;
    int page_num;
    int num_pages;
    int line;
    int font_size;
    int lines_per_page;
    int num_styles;
    PsStyle *styles;
    PsColor default_foreground;
    struct
    {
    	int top, left, width, height;
    } pagebox;
};


#define LINE_SPACING 2
#define INDENT 36   	/* 1/2 in */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

PsDocument *
ps_begin(FILE *fp)
{
    PsDocument *ps;
    
    ps = g_new(PsDocument, 1);
    memset(ps, 0, sizeof(PsDocument));
    
    ps->fp = fp;
    ps->in_page = FALSE;
    ps->page_num = 0;
    ps->num_pages = 0;
    ps->line = 0;
    ps->font_size = 10;
    ps->num_styles = 0;
    ps->styles = 0;
    
    ps->default_foreground.defined = TRUE;
    ps->default_foreground.red = 0.0;
    ps->default_foreground.green = 0.0;
    ps->default_foreground.blue = 0.0;
    
    ps->lines_per_page = ((prefs.paper_height - prefs.margin_top - prefs.margin_bottom)
    	    	      / (ps->font_size + LINE_SPACING));

    ps->pagebox.left = prefs.margin_left;
    ps->pagebox.top = prefs.margin_top,
    ps->pagebox.width = prefs.paper_width - prefs.margin_left - prefs.margin_right,
    ps->pagebox.height = prefs.paper_height - prefs.margin_top - prefs.margin_bottom;

    return ps;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/* TODO: landscape option? */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

void
ps_title(PsDocument *ps, const char *title)
{
    if (ps->title != 0)
    	g_free(ps->title);
    ps->title = g_strdup(title);
}

void
ps_num_lines(PsDocument *ps, int n)
{
    ps->num_pages = (n + ps->lines_per_page - 1) / ps->lines_per_page;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static void
ps_expand_styles(PsDocument *ps, int n)
{
    if (n > ps->num_styles)
    {
    	PsStyle *old = ps->styles;
	
	ps->styles = g_new(PsStyle, n);
	memset(ps->styles, 0, sizeof(PsStyle)*n);
	if (old != 0)
	{
	    memcpy(ps->styles, old, sizeof(PsStyle)*ps->num_styles);
	    g_free(old);
	}
	ps->num_styles = n;
    }
}

void
ps_foreground(PsDocument *ps, int style, float r, float g, float b)
{
    PsStyle *s;
    
    ps_expand_styles(ps, style+1);
    s = &ps->styles[style];
    s->foreground.defined = TRUE;
    s->foreground.red = r;
    s->foreground.green = g;
    s->foreground.blue = b;
}

void
ps_background(PsDocument *ps, int style, float r, float g, float b)
{
    PsStyle *s;
    
    ps_expand_styles(ps, style+1);
    s = &ps->styles[style];
    s->background.defined = TRUE;
    s->background.red = r;
    s->background.green = g;
    s->background.blue = b;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static char *
ps_date_string(void)
{
    char *datebuf;
    time_t clock;

    time(&clock);
    datebuf = ctime(&clock);
    datebuf[24] = '\0';
    return g_strdup(datebuf);
}

static const char prologue[] = "\
%%EndComments\n\
%%BeginProlog\n\
/MaketoolDict 100 dict def\n\
MaketoolDict begin\n\
/Helvetica-Roman findfont 10 scalefont /_f exch def\n\
/bd{bind def}bind def\n\
/m{moveto}bd\n\
/l{lineto}bd\n\
/b{4 dict begin\n\
 /h exch def /w exch def /y exch def /x exch def\n\
 x y moveto x w add y lineto x w add y h add lineto x y h add lineto\n\
 closepath\n\
 end}bd\n\
/pb{4 copy b clip 4 copy b 0 0 0 setrgbcolor fill b 1 1 1 setrgbcolor fill}bd\n\
/pa{b 0 0 0 setrgbcolor stroke}bd\n\
/s{stroke}bd\n\
/f{fill}bd\n\
/t{show}bd\n\
/SN{/_N exch def\n\
 /_fg _N array def\n\
 /_bg _N array def\n\
 0 1 _N 1 sub{_fg exch [0 0 0] put}for\n\
 0 1 _N 1 sub{_bg exch [1 1 1] put}for\n\
}bd\n\
/SF{3 array astore _fg 3 1 roll put}bd\n\
/SB{3 array astore _bg 3 1 roll put}bd\n\
/F{_fg exch get aload pop setrgbcolor}bd\n\
/B{_bg exch get aload pop setrgbcolor}bd\n\
%multiple fills work around ghostscript 6.52 bug\n\
/bp{_f setfont 0 0 0 setrgbcolor 1 setlinewidth}bd\n\
/ep{showpage}bd\n\
end\n\
%%EndProlog\n\
";

void
ps_prologue(PsDocument *ps)
{
    int i;
    char *date;
    
    fprintf(ps->fp, "%%!PS-Adobe-2.0\n");
    fprintf(ps->fp, "%%%%Creator: Maketool %s, Copyright (c) 1999-2003 Greg Banks. All Rights Reserved.\n", VERSION); 
    assert(ps->num_pages > 0);
    fprintf(ps->fp, "%%%%Pages: %d\n", ps->num_pages); 
    fprintf(ps->fp, "%%%%Title: %s\n", ps->title);
    date = ps_date_string();
    fprintf(ps->fp, "%%%%CreationDate: %s\n", date); 
    g_free(date);
    fputs(prologue, ps->fp);
    
    
    fprintf(ps->fp, "%%%%BeginSetup\n");
    fprintf(ps->fp, "MaketoolDict begin\n");
    fprintf(ps->fp, "%d SN\n", ps->num_styles);
    for (i=0 ; i<ps->num_styles ; i++)
    {
    	if (ps->styles[i].foreground.defined)
	    fprintf(ps->fp, "%d %.3g %.3g %.3g SF\n",
	    	i,
	    	ps->styles[i].foreground.red,
	    	ps->styles[i].foreground.green,
	    	ps->styles[i].foreground.blue);
    	if (ps->styles[i].background.defined)
	    fprintf(ps->fp, "%d %.3g %.3g %.3g SB\n",
	    	i,
	    	ps->styles[i].background.red,
	    	ps->styles[i].background.green,
	    	ps->styles[i].background.blue);
    }
    fprintf(ps->fp, "end %%MaketoolDict\n");
    fprintf(ps->fp, "%%%%EndSetup\n");
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/* TODO: save & restore stuff */
static const char page_setup[] = "\
%%BeginPageSetup\n\
MaketoolDict begin\n\
bp\n\
%%EndPageSetup\n\
";

static const char page_trailer[] = "\
%%BeginPageTrailer\n\
ep\n\
end %MaketoolDict\n\
%%EndPageTrailer\n\
";

static void
ps_end_page(PsDocument *ps)
{
    if (ps->in_page)
    {
	/* Draw a box around the usable area */
	fprintf(ps->fp, "%d %d %d %d pa\n",
    	    ps->pagebox.left, ps->pagebox.top,
	    ps->pagebox.width, ps->pagebox.height);
	fputs(page_trailer, ps->fp);
    }
    ps->in_page = FALSE;
}

static void
ps_begin_page(PsDocument *ps, int n)
{
    fprintf(ps->fp, "%%%%Page: %d %d\n", n, n);
    fputs(page_setup, ps->fp);
    
    /* TODO: draw page/numpages outside usable area */

    /* Clip to the usable area */
    fprintf(ps->fp, "%d %d %d %d pb\n",
    	ps->pagebox.left, ps->pagebox.top,
	ps->pagebox.width, ps->pagebox.height);
    
    ps->page_num = n;
    ps->in_page = TRUE;
}

static void
ps_next_page(PsDocument *ps)
{
    /* End the previous page if any */
    ps_end_page(ps);
    ps_begin_page(ps, ps->page_num+1);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static void
ps_put_escaped_string(PsDocument *ps, const char *text)
{
    static const char escapable[] = "\n\r\t\b\f\\()";
    
    for ( ; *text ; text++)
    {
    	if (strchr(escapable, *text) != 0)
	    fprintf(ps->fp, "\\%03o", (int)*text);
	else
	    fputc(*text, ps->fp);
    }
}

void
ps_line(
    PsDocument *ps,
    const char *text,
    int style,
    int indent_level)
{
    double x, y;
    
    if (style < 0 || style >= ps->num_styles)
    	style = 0;  	/* default style */
	
    if (ps->line == 0)
    	ps_next_page(ps);

    y = prefs.paper_height - prefs.margin_bottom -
    	    	    (ps->line+1) * (ps->font_size + LINE_SPACING);
    x = prefs.margin_left + indent_level * INDENT;

    if (ps->styles[style].background.defined)
    {
	/* set to background colour */
	fprintf(ps->fp, "%d B\n", style);
	/* box around line */
	fprintf(ps->fp, "%d %d %d %d b f\n",
    	    prefs.margin_left,
	    (int)y-LINE_SPACING/2-(int)(0.3 * ps->font_size),  /* hack for descenders */
	    prefs.paper_width - prefs.margin_left - prefs.margin_right,
	    ps->font_size + LINE_SPACING);
    }
    
    /* set to foreground colour */
    fprintf(ps->fp, "%d F\n", style);
    fprintf(ps->fp, "%g %g m\n", x, y);
    fprintf(ps->fp, "(");
    ps_put_escaped_string(ps, text);
    fprintf(ps->fp, ") t\n");

    if (++ps->line == ps->lines_per_page)
    	ps->line = 0;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

/* TODO: save& restore stuff */
static const char trailer[] = "\
%%Trailer\n\
%%EOF\n\
";

void    
ps_end(PsDocument *ps)
{
    ps_end_page(ps);
    fputs(trailer, ps->fp);
    fflush(ps->fp);

    if (ps->title != 0)
    	g_free(ps->title);    
    g_free(ps);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*END*/


syntax highlighted by Code2HTML, v. 0.9.1