/* p_image.c
 * Copyright (C) 1997-98 Thomas Merz. All rights reserved.
 *
 * PDFlib image routines
 */

#include <stdio.h>

#include "p_intern.h"

void
PDF_place_image(PDF *p, PDF_image *image, float x, float y, float scale)
{
    PDF_put_image(p, image);
    PDF_execute_image(p, image, x, y, scale);
}

void
PDF_put_image(PDF *p, PDF_image *image)
{
    PDF_data_source	src;

    id			image_id, length_id, colormap_id = 0;
    long		length;

    switch (image->colorspace) {
        case DeviceGray:
        case DeviceRGB:
        case DeviceCMYK:
	    break;

	default:
	    pdf_error(p, PDF_INTERNAL, "Bad number of color components");
	    break;
    }

    pdf_end_contents_section(p);

    /* Write colormap information for indexed color spaces */
    if (image->indexed) {
	colormap_id = pdf_begin_obj(p, NEW_ID);	/* Colormap object */
	pdf_begin_dict(p); 		
	    if (!p->info->binary_mode) {
#ifdef DEBUG
		(void) fputs("/Filter [/ASCIIHexDecode]\n", p->fp);
#else
		(void) fputs("/Filter [/ASCII85Decode]\n", p->fp);
#endif
	    }

	    /* Length of colormap object */
	    length_id = pdf_alloc_id(p);
	    (void) fprintf(p->fp,"/Length %ld 0 R\n", length_id);
	pdf_end_dict(p);

	pdf_begin_stream(p);
	p->start_contents_pos = ftell(p->fp);

	if (image->components != 1 && image->components != 3) {
	    pdf_error(p, PDF_FATAL, "Bogus indexed colorspace");
	}

	/* Write colormap data */
	if (!p->info->binary_mode) {
	    /* use another data source than GIF image data source! */
	    PDF_data_source_from_buf(p, &src, (byte *) image->colormap,
	    (long) image->BitPixel * 3); 	/* HACK */
#ifdef DEBUG
	    pdf_ASCIIHexEncode(p, &src);
#else
	    pdf_ASCII85Encode(p, &src);
#endif
	} else {
	    fwrite(image->colormap, image->BitPixel * 3, 1, p->fp); 	/* HACK */
	    (void) fputs("\n", p->fp);
	}

	length = ftell(p->fp) - p->start_contents_pos;

	pdf_end_stream(p);
	pdf_end_obj(p);

	pdf_begin_obj(p, length_id);		/* Length object for colormap */
	(void) fprintf(p->fp,"%ld\n", length);
	pdf_end_obj(p);
    }

    image_id = pdf_begin_obj(p, NEW_ID);	/* Image object */
    pdf_add_res_xobject(p, image_id);

    length_id		= pdf_alloc_id(p);

    pdf_begin_dict(p); 		

    image->no = p->image_number++;
    (void) fputs("/Type /XObject\n", p->fp);
    (void) fputs("/Subtype /Image\n", p->fp);
    (void) fprintf(p->fp,"/Name /I%d\n", image->no);
    (void) fprintf(p->fp,"/Width %d\n", image->width);
    (void) fprintf(p->fp,"/Height %d\n", image->height);
    (void) fprintf(p->fp,"/BitsPerComponent %d\n", image->bpc);

    (void) fputs("/ColorSpace ", p->fp);
    if (image->indexed) {
	(void) fputs("[/Indexed ", p->fp);
	(void) fprintf(p->fp, "/%s %d %ld 0 R ]\n",
		(image->components == 3 || image->indexed ? 
		pdf_colorspace_names[DeviceRGB] :
		pdf_colorspace_names[DeviceGray]),
		image->BitPixel - 1, colormap_id);
    } else {
	(void) fprintf(p->fp, "/%s\n", pdf_colorspace_names[image->colorspace]);
    }

    /* do we need a Filter (either ASCII or decompression)? */
    if (!p->info->binary_mode || image->compression != none)
	(void) fputs("/Filter [", p->fp);

    if (!p->info->binary_mode) {
#ifdef DEBUG
	(void) fputs("/ASCIIHexDecode ", p->fp);
#else
	(void) fputs("/ASCII85Decode ", p->fp);
#endif
    }

    if (image->compression != none)
	(void) fprintf(p->fp, "/%s", pdf_filter_names[image->compression]);

    if (!p->info->binary_mode || image->compression != none)
	(void) fprintf(p->fp, "]\n");

    if (image->compression == lzw) {
	if (p->info->binary_mode)
	    (void) fprintf(p->fp, "/DecodeParms <</EarlyChange 0>>\n");
	else
	    (void) fprintf(p->fp, 
		"/DecodeParms [ null <</EarlyChange 0>>]\n");
    }

    (void) fprintf(p->fp,"/Length %ld 0 R\n", length_id);
    pdf_end_dict(p);

    pdf_begin_stream(p);
    p->start_contents_pos = ftell(p->fp);

    /* image data */
    if (p->info->binary_mode)
    {
	image->src.init(p, &image->src);

	while (image->src.fill(p, &image->src)) {
	    (void) fwrite(image->src.next_byte, 1, 
			image->src.bytes_available, p->fp);
	}
	image->src.terminate(p, &image->src);
    }
    else
    {
#ifdef DEBUG
	pdf_ASCIIHexEncode(p, &image->src);
#else
	pdf_ASCII85Encode(p, &image->src);
#endif
    }

    length = ftell(p->fp) - p->start_contents_pos;

    pdf_end_stream(p);
    pdf_end_obj(p);

    pdf_begin_obj(p, length_id);		/* Length object */
    (void) fprintf(p->fp,"%ld\n", length);
    pdf_end_obj(p);
}

/* Section 8.8 XObject operator */

void
PDF_execute_image(PDF *p, PDF_image *image, float x, float y, float scale)
{
    PDF_matrix m;

    if (scale == 0.0) {
	pdf_error(p, PDF_FATAL, "Scale factor 0 for image %s", image->filename);
	return;
    }

    if (image->components == 3 || image->indexed)
	p->res.procset	|= ImageC;
    else
	p->res.procset	|= ImageB;

    pdf_end_text(p);
    pdf_begin_contents_section(p);

    PDF_save(p);

    m.a = image->width * scale;
    m.d = image->height * scale;
    m.b = m.c = 0.0;
    m.e = x;
    m.f = y;
    pdf_concat(p, m);

    /* todo: image name */ 	/* HACK */
    (void) fprintf(p->fp,"/I%d Do\n", image->no);

    PDF_restore(p);
}

/* Section 8.9 In-line image operators */

void
PDF_place_inline_image(PDF *p, PDF_image *image, float x, float y, float scale)
{
    PDF_matrix m;

    p->res.procset |= (image->colorspace == DeviceGray ? ImageB : ImageC);

    pdf_end_text(p);
    PDF_save(p);

    m.a = image->width * scale;
    m.d = image->height * scale;
    m.b = m.c = 0.0;
    m.e = x;
    m.f = y;

    pdf_concat(p, m);

    (void) fputs("BI\n", p->fp);
    (void) fprintf(p->fp,"/W %d\n", image->width);
    (void) fprintf(p->fp,"/H %d\n", image->height);
    (void) fprintf(p->fp,"/BPC %d\n", image->bpc);

    (void) fputs("/CS ", p->fp);
    switch (image->colorspace) {
	case DeviceGray:
	    if (image->components != 1)
		pdf_error(p, PDF_FATAL,
		    "Invalid number of color components for /DeviceGray!");
	    (void) fputs("/G\n", p->fp);
	    break;

	case DeviceRGB:
	    if (image->components != 3)
		pdf_error(p, PDF_FATAL, 
		    "Invalid number of color components for /DeviceRGB!");
	    (void) fputs("/RGB\n", p->fp);
	    break;

	case DeviceCMYK:
	    if (image->components != 4)
		pdf_error(p, PDF_FATAL, 
		    "Invalid number of color components for /DeviceCMYK!");
	    (void) fputs("/CMYK\n", p->fp);
	    break;

	case Indexed:
	    pdf_error(p, PDF_FATAL,
	    	"Indexed color space for inline images NYI!");
	    break;

	default:
	    pdf_error(p, PDF_FATAL,
	    	"Unknown color space for inline images!");
	    break;
    }

    if (!p->info->binary_mode)
#ifdef DEBUG
	(void) fputs("/F /AHx\n", p->fp);
#else
	(void) fputs("/F /A85\n", p->fp);
#endif
    (void) fputs("ID ", p->fp);

    if (p->info->binary_mode)
    {
	image->src.init(p, &image->src);
	while (image->src.fill(p, &image->src)) {
	    (void) fwrite(image->src.next_byte, 1, 
			image->src.bytes_available, p->fp);
	}
	image->src.terminate(p, &image->src);
    }
    else
    {
#ifdef DEBUG
	pdf_ASCIIHexEncode(p, &image->src);
#else
	pdf_ASCII85Encode(p, &image->src);
#endif
    }

    (void) fputs("EI\n", p->fp);
    PDF_restore(p);
}

void
PDF_close_image(PDF *p, PDF_image *image)
{
    image->closefunc(p, image);
}
