/* pdfdemo.c
 * Copyright (C) 1997-98 Thomas Merz. All rights reserved.
 *
 * Test bed and sample application for PDFlib
 */

#ifdef DOS
#include <process.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "pdf.h"

/* ------------------------------------------------------------- */
static void
gif_image(PDF *p)
{
    PDF_image	*image;

#define GIFFILE		"BINDINGS/C/acro_web.gif"

    if ((image = PDF_open_GIF(p, GIFFILE)) == NULL) {
	fprintf(stderr, "Error: Couldn't analyze GIF image %s.\n", GIFFILE);
	return;
    }

    PDF_begin_page(p, image->width, image->height);
    PDF_add_outline(p, "GIF image");

    PDF_place_image(p, image, 0.0, 0.0, 1.0);
    PDF_close_GIF(p, image);

    PDF_end_page(p);

#undef GIFFILE
}

/* ------------------------------------------------------------- */
#ifdef USE_TIFF
static void
tiff_image(PDF *p)
{
    PDF_image	*image;

#define TIFFFILE	"BINDINGS/C/bible.tif"

    if ((image = PDF_open_TIFF(p, TIFFFILE)) == NULL) {
	fprintf(stderr, "Error: Couldn't analyze TIFF image %s.\n", TIFFFILE);
	return;
    }

    PDF_begin_page(p, image->width, image->height);
    PDF_add_outline(p, "TIFF image");

    PDF_place_image(p, image, 0.0, 0.0, 1.0);
    PDF_close_TIFF(p, image);

    PDF_end_page(p);

#undef TIFFFILE
}
#endif

/* ------------------------------------------------------------- */
static void
raster_image_modified(PDF *p)
{
    PDF_image	*image;
    float	scale;

#define JPEGFILE	"BINDINGS/C/nesrin.jpg"

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Transformed JPEG images");

    if ((image = PDF_open_JPEG(p, JPEGFILE)) == NULL) {
	fprintf(stderr, "Error: Couldn't analyze JPEG image %s.\n", JPEGFILE);
	return;
    }
    /* Put image data in PDF file and re-use with different transformations */
    PDF_put_image(p, image);

    /* ----------------- first image ------------------- */
    scale = a4.width/image->width;	/* fit image to page width */
    PDF_execute_image(p, image, 0.0,  a4.height-image->height*scale, scale);

    /* ----------------- second image ------------------- */
    scale = 0.5;
    PDF_save(p);
    PDF_rotate(p, 90.0);
    PDF_execute_image(p, image, 0, -a4.width, scale);
    PDF_restore(p);

    /* ----------------- third image ------------------- */
    scale = 0.25;
    PDF_save(p);
    PDF_rotate(p, 45.0);
    PDF_execute_image(p, image, 200, 0.0, scale);
    PDF_restore(p);

    PDF_close_JPEG(p, image);
    PDF_end_page(p);

#undef JPEGFILE
}

/* ------------------------------------------------------------- */
static void
character_table(PDF *p)
{
    char	text[50];
    int		i, j;
    float	x, y;

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Character table");

#define LEFT		50
#define TOP		700
#define FONTSIZE	16
#define FONTNAME	"Times-Roman"

    PDF_set_font(p, FONTNAME, 2*FONTSIZE, winansi);
    PDF_show_xy(p, "ISOLatin1Encoding", LEFT, TOP+2*FONTSIZE);

    PDF_set_font(p, FONTNAME, FONTSIZE, winansi);
    text[1] = 0;

    y = TOP;
    for (i = 2; i < 16; i++) {
	y -=  2*FONTSIZE;
	x = LEFT;
	for (j = 0; j < 16; j++) {
	    text[0] = i*16 + j;
	    PDF_show_xy(p, text, x, y);
	    x += 2*FONTSIZE;
	}
    }

    PDF_end_page(p);

#undef LEFT
#undef TOP
#undef FONTSIZE
#undef FONTNAME
}

/* ------------------------------------------------------------- */
static void
grid(PDF *p)
{
#define STEP		10
#define FONTSIZE	10.0
#define THICK		1.0
#define THIN		0.01

    char buf[10];
    float i, width = a4.width, height = a4.height;

    PDF_begin_page(p, width, height);
    PDF_add_outline(p, "Grid");

    PDF_setlinewidth(p, THIN);
    PDF_setdash(p, 1.0, 2.0);

    /* draw vertical lines */
    for (i = 0; i < width; i += STEP) {
	PDF_save(p);
	if ((int) i % 100 == 0)
	    PDF_setlinewidth(p, THICK);
	if ((int) i % 50 == 0)
	    PDF_setdash(p, 0.0, 0.0);
	PDF_moveto(p, i, 0);
	PDF_lineto(p, i, height);
	PDF_stroke(p);
	PDF_restore(p);
    }

    /* draw horizontal lines */
    for (i = 0; i < height; i += STEP) {
	PDF_save(p);
	if ((int) i % 50 == 0)
	    PDF_setdash(p, 0.0, 0.0);
	if ((int) i % 100 == 0)
	    PDF_setlinewidth(p, THICK);
	PDF_moveto(p, 0, i);
	PDF_lineto(p, width, i);
	PDF_stroke(p);
	PDF_restore(p);
    }

#define FONTNAME	"Helvetica-Bold"
#define DELTA	9
#define RADIUS	12.0

    PDF_set_font(p, FONTNAME, FONTSIZE, winansi);

    /* print captions */
    for (i = 100; i < width; i+= 100) {
	PDF_save(p);
	PDF_setgray_fill(p, 1.0);
	PDF_circle(p, i, 20.0, RADIUS);
	PDF_fill(p);
	PDF_restore(p);
	sprintf(buf, "%d", (int) i);
	PDF_show_xy(p, buf, i - DELTA, 20.0 - FONTSIZE/3);
    }

    for (i = 100; i < height; i+= 100) {
	PDF_save(p);
	PDF_setgray_fill(p, 1.0);
	PDF_circle(p, 40.0, i, RADIUS);
	PDF_fill(p);
	PDF_restore(p);
	sprintf(buf, "%d", (int) i);
	PDF_show_xy(p, buf, 40.0 - DELTA, i - FONTSIZE/3);
    }

    PDF_end_page(p);

#undef STEP
#undef FONTSIZE
#undef DELTA
#undef RADIUS
#undef FONTNAME
}

/* ------------------------------------------------------------- */
static void
shaded_circle(PDF *p)
{
    int i;

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Shaded circle");

    for (i = 255; i >= 0; i -= 3) {
	PDF_setrgbcolor_fill(p, 1.0 - (float) i/255, 1.0 - (float) i/255, 1.0);
	PDF_circle(p, 300, 400, i);
	PDF_fill(p);
    }
    PDF_end_page(p);
}


/* ------------------------------------------------------------- */
static void
shaded_circle_linear(PDF *p)
{
    int i, step = 1;
    float gray = 0.1, r;

    r = pow(1.0/gray, 1.0/255.0); /* generate perceptual linear color blend */
    r = pow(r, step);		  /* skip steps to improve performance */

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Shaded circle (linearized)");

    for (i = 255; i >= 0; i -= step) {
	PDF_setrgbcolor_fill(p, gray, gray, 1.0);
	PDF_circle(p, 300, 400, i);
	PDF_fill(p);
	gray *= r;
    }
    PDF_end_page(p);
}

/* ------------------------------------------------------------- */
static void
centered_text(PDF *p)
{
    float	y, width;
    char	**cp;
    char	*text[] = {
		    "Hat der alte Hexenmeister",
		    "Sich doch einmal wegbegeben!",
		    "Und nun sollen seine Geister",
		    "Auch nach meinem Willen leben.",
		    "Seine Wort' und Werke",
		    "Merkt ich und den Brauch,",
		    "Und mit Geistesst\344rke",
		    "Tu ich Wunder auch.",
		    "Walle! walle",
		    "Manche Strecke,",
		    "Da\337, zum Zwecke,",
		    "Wasser flie\337e",
		    "Und mit reichem, vollem Schwalle",
		    "Zu dem Bade sich ergie\337e.",
		    NULL };

#define FONTSIZE	24
#define FONTNAME	"Times-Roman"
#define CENTER		300
#define TOP		750

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Centered text");

    PDF_set_font(p, FONTNAME, FONTSIZE, winansi);

    y = TOP;

    for (cp = text; *cp; cp++) {
	width = PDF_stringwidth(p, (unsigned char *) *cp);
	PDF_show_xy(p, *cp, CENTER - width/2, y);
	y -= 1.5 * FONTSIZE;
    }

    PDF_end_page(p);

#undef FONTSIZE
#undef FONTNAME
#undef RIGHT
#undef TOP
}

/* ------------------------------------------------------------- */
static void
inline_image(PDF *p)
{
    byte		*buf, *bp;
    long		buflen;
    PDF_image		image;
    int			i, j;
    float		y, sx, sy;

#define LEFT		50.0

    PDF_begin_page(p, 900.0, 600.0);
    PDF_add_outline(p, "Inline image");

    /* 
     * Since the image interface doesn't support non-proportional
     * scaling, we will use PDF_scale() instead to stretch the image.
     */
    image.width		= 256;
    image.height	= 1;
    image.bpc		= 8;
    image.components	= 3;
    image.colorspace	= DeviceRGB;

    sx = 3.0;	/* desired horizontal scaling factor */
    sy = 128;	/* desired height of one color band */

    buflen = image.width * image.height * image.components;
    buf = PDF_malloc(buflen, "inline_image");

    if (buf == NULL) {
	fprintf(stderr, "Not enough memory for inline image!\n");
	return;
    }

    /* 
     * In positioning the images below, we will have to compensate 
     * for the scaling.
     */
    PDF_scale(p, sx, sy);		/* stretch image */

    /* now fill the buffer with fake image data (simple color ramp) */
    for (bp = buf, i=0; i<image.height; i++) {
	for (j=0; j<image.width; j++) {
	    *bp++ = (byte) (j % 256);	/* red blend */
	    *bp++ = 0;
	    *bp++ = 0;
	}
    }

    y = LEFT;
    PDF_data_source_from_buf(p, &image.src, buf, buflen);
    PDF_place_inline_image(p, &image, LEFT/sx, y/sy, 1.0);

    for (bp = buf, i=0; i<image.height; i++) {
	for (j=0; j<image.width; j++) {
	    *bp++ = 0;
	    *bp++ = (byte) (j % 256);	/* green blend */
	    *bp++ = 0;
	}
    }

    y += image.height * sy;		/* position the image */
    PDF_data_source_from_buf(p, &image.src, buf, buflen);
    PDF_place_inline_image(p, &image, LEFT/sx, y/sy, 1.0);

    for (bp = buf, i=0; i<image.height; i++) {
	for (j=0; j<image.width; j++) {
	    *bp++ = 0;
	    *bp++ = 0;
	    *bp++ = (byte) (j % 256);	/* blue blend */
	}
    }

    y += image.height * sy;		/* position the image */
    PDF_data_source_from_buf(p, &image.src, buf, buflen);
    PDF_place_inline_image(p, &image, LEFT/sx, y/sy, 1.0);

    image.components	= 1;		/* now a single component image */
    image.colorspace	= DeviceGray;

    for (bp = buf, i=0; i<image.height; i++) {
	for (j=0; j<image.width; j++) {
	    *bp++ = (byte) (j % 256);	/* gray blend */
	}
    }

    y += image.height * sy;		/* position the image */
    buflen = image.width * image.height * image.components;
    PDF_data_source_from_buf(p, &image.src, buf, buflen);
    PDF_place_inline_image(p, &image, LEFT/sx, y/sy, 1.0);

    PDF_free(buf);
    PDF_end_page(p);
#undef LEFT
}

/* ------------------------------------------------------------- */
static void
radial_structure(PDF *p)
{
    float alpha;

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Radial structure");

    PDF_translate(p, 300, 400);
    PDF_setlinewidth(p, 0.1);

#ifdef NOT_EXACT
    /* 
     * Mathematically, the following are equivalent. However, due
     * to massive accumulation of rounding errors the first variant
     * produces visible inaccuracy artifacts (try it!)
     */
    for (alpha = 0; alpha < 360; alpha++) {
	PDF_moveto(p, 0.0, 0.0);
	PDF_lineto(p, 250.0, 0.0);
	PDF_stroke(p);
	PDF_rotate(p, 1.0);
    }
#endif

    /* better solution: don't accumulate rounding errors */
    for (alpha = 0; alpha < 360; alpha++) {
	PDF_save(p);
	PDF_rotate(p, alpha);
	PDF_moveto(p, 0.0, 0.0);
	PDF_lineto(p, 250.0, 0.0);
	PDF_stroke(p);
	PDF_restore(p);
    }

    PDF_end_page(p);
}

/* ------------------------------------------------------------- */
static void
random_data_graph(PDF *p)
{
    float x;

    PDF_begin_page(p, a4.width, a4.height);
    PDF_add_outline(p, "Random graph");

#define STEP	10.0
#define MARGIN	50.0
#define RIGHT	500.0
#define TOP	800.0
    PDF_setlinewidth(p, 2);
    PDF_moveto(p, RIGHT, MARGIN);
    PDF_lineto(p, MARGIN, MARGIN);
    PDF_lineto(p, MARGIN, TOP);
    PDF_setgray_stroke(p, 0);		/* black */
    PDF_stroke(p);

    PDF_setlinewidth(p, 1);
    PDF_moveto(p, MARGIN, MARGIN);

    /* construct some random graph data */
    PDF_setrgbcolor_stroke(p, 1.0, 0, 0);	/* red */
    for (x=MARGIN; x<RIGHT; x+=STEP)
	PDF_lineto(p, x, x + (TOP-MARGIN)/2.0*rand()/(RAND_MAX+1.0));

    PDF_stroke(p);

    PDF_setrgbcolor_stroke(p, 0, 1.0, 0);	/* green */
    PDF_moveto(p, MARGIN, MARGIN);
    for (x=MARGIN; x<RIGHT; x+=STEP)
	PDF_lineto(p, x, 
	    MARGIN + TOP*(x-MARGIN)*(x-MARGIN)/((RIGHT-MARGIN)*(RIGHT-MARGIN)));

    PDF_stroke(p);

    PDF_setrgbcolor_stroke(p, 0, 0, 1.0);	/* blue */
    PDF_moveto(p, MARGIN, MARGIN);
    for (x=MARGIN; x<RIGHT; x+=STEP)
	PDF_lineto(p, x, MARGIN + x + MARGIN*rand()/(RAND_MAX+1.0));

    PDF_stroke(p);

    PDF_end_page(p);

#undef STEP
#undef MARGIN
#undef RIGHT
#undef TOP
}

void
main(int argc, char *argv[])
{
    char	*filename;
    FILE	*pdffile;
    PDF_info	*info;
    PDF		*p;

    if (argc < 2)
	filename = "demo.pdf";
    else
	filename = argv[1];

    if ((pdffile = fopen(filename, WRITEMODE)) == NULL) {
    	fprintf(stdout, "Couldn't open PDF file '%s'!\n", filename);
	exit(1);
    }

    info = PDF_get_info();
    info->Creator	= "PDFdemo";
    info->Title		= "PDFlib demo application";
    info->Subject	= "Check many PDFlib function calls";
    info->Author	= "Thomas Merz";
    info->binary_mode   = false;

    p = PDF_open(pdffile, info);

#ifdef USE_TIFF
    tiff_image(p);
#endif

    gif_image(p);
    raster_image_modified(p);
    inline_image(p);
    centered_text(p);
    character_table(p);
    grid(p);
    shaded_circle(p);
    shaded_circle_linear(p);
    radial_structure(p);
    random_data_graph(p);

    PDF_close(p);

    exit(0);
}
