/* p_filter.c
 * Copyright (C) 1997-98 Thomas Merz. All rights reserved.
 *
 * ASCII85 and Hex encoding for PDFlib 
 */

#include <stdio.h>
#include <fcntl.h>

#include "p_intern.h"

#ifdef DOS
#include <io.h>
#include <stdlib.h>
#endif

static const unsigned long power85[5] = 
	{ 1L, 85L, 85L*85, 85L*85*85, 85L*85*85*85};

/* Auxiliary routines */

/* output one ASCII byte and keep track of characters per line */
static void 
pdf_outbyte(PDF *p, byte c)
{
    if (fputc(c, p->fp) == EOF)
	pdf_error(p, PDF_FATAL, "Write error in pdf_outbyte()");

#define MAX_CHARS_PER_LINE	64
    /* insert line feed */
    if (++(p->chars_on_this_line) == MAX_CHARS_PER_LINE)
    {
	fputc('\n', p->fp);
	p->chars_on_this_line = 0;
    }
}

void
pdf_ASCII85Encode(PDF *p, PDF_data_source *src)
{
    unsigned long word, v;
    int i, fetched;
    byte buf[4];

    src->init(p, src);
    if (!src->fill(p, src)) {
	pdf_error(p, PDF_WARN, "Data underrun in pdf_ASCII85Encode");
	fputs("~>\n", p->fp);			/* EOD marker */
	return;
    }

    p->chars_on_this_line = 0;

    while (1)
    {
	for (fetched = 0; fetched < 4; fetched++)
	{
	    if (src->bytes_available == 0 && !src->fill(p, src))
		break;
	    buf[fetched] = *(src->next_byte);
	    src->next_byte++;
	    src->bytes_available--;
	}
	if (fetched < 4)
	    break;

	/* 4 bytes available ==> output 5 bytes */
	word = ((unsigned long)(((unsigned int)buf[0] << 8) + buf[1]) << 16) +
	       (((unsigned int)buf[2] << 8) + buf[3]);
	if (word == 0)
	    pdf_outbyte(p, (byte) 'z');       /* shortcut for 0 */
	else
	{
	    /* calculate 5 ASCII85 bytes and output them */
	    for (i = 4; i >= 0; i--) {
		v = word / power85[i];
		pdf_outbyte(p, (byte)v + '!');
		word -= v * power85[i];
	    }
	}
    }

    word = 0;

    /* 0-3 bytes left */
    if (fetched != 0)
    {
	for (i = fetched - 1; i >= 0; i--)   /* accumulate bytes */
	    word += (unsigned long)buf[i] << 8 * (3-i);

	/* encoding as above, but output only fetched+1 bytes */
	for (i = 4; i >= 4-fetched; i--)
	{
	    v = word / power85[i];
	    pdf_outbyte(p, (byte)v + '!');
	    word -= v * power85[i];
	}
    }

    src->terminate(p, src);

    fputs("~>\n", p->fp);			/* EOD marker */
}

void 
pdf_ASCIIHexEncode(PDF *p, PDF_data_source *src)
{
    static const char BinToHex[] = "0123456789ABCDEF";
    int CharsPerLine;
    long i;
    byte *data;

    CharsPerLine = 0;

    src->init(p, src);

    while (src->fill(p, src))
    {
	
	for (data=src->next_byte, i=src->bytes_available; i > 0; i--, data++)
	{
	  putc(BinToHex[*data>>4], p->fp);           /* first nibble  */
	  putc(BinToHex[*data & 0x0F], p->fp);       /* second nibble */
	  if ((CharsPerLine += 2) >= 64) {
	    putc('\n', p->fp);
	    CharsPerLine = 0;
	  }
	}
    }

    src->terminate(p, src);
    (void) fputs(">\n", p->fp);         /* EOD marker for PDF hex strings */
}

/* methods for constructing a data source from a memory buffer */

/* dummy for use in PDF_data_source */
static void
pdf_noop(PDF *p, PDF_data_source *src)
{
}

static bool
pdf_data_source_buf_fill(PDF *p, PDF_data_source *src)
{
    if (src->next_byte == NULL) {
	src->next_byte		= src->buffer_start;
	src->bytes_available	= src->buffer_length;
	return true;
    }

    return false;
}

void
PDF_data_source_from_buf(PDF *p, PDF_data_source *src, byte *buffer, long len)
{
    src->init		= pdf_noop;
    src->fill		= pdf_data_source_buf_fill;
    src->terminate	= pdf_noop;

    src->buffer_start	= buffer;
    src->buffer_length	= len;

    src->bytes_available= 0;
    src->next_byte	= NULL;
}
