/* Copyright (C) 1999, 2000 artofcode LLC.  All rights reserved.
  
  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.

*/

/*$Id: gdevpdfo.h,v 1.6.4.1.2.1 2003/01/17 00:49:01 giles Exp $ */
/* Internal definitions for "objects" for pdfwrite driver. */

#ifndef gdevpdfo_INCLUDED
#  define gdevpdfo_INCLUDED

/*
 * This file defines the structures and support procedures for what Adobe
 * calls "Cos objects".  (We don't know what "Cos" stands for, but in our
 * case, it's a good acronym for "Collection and stream".)  We only
 * support arrays, dictionaries, and streams: we merge all other types into a
 * single string type, which holds the printed representation that will be
 * written on the output file, and we only support that type as an element
 * of the collection types.
 *
 * Note that Cos objects are *not* reference-counted.  Objects without
 * an ID are assumed to be referenced from only one place; objects with
 * IDs are managed manually.  We also assume that strings (the keys of
 * dictionaries, and non-object values in arrays and dictionaries) are
 * "owned" by their referencing object.
 */

#include "gsparam.h"

/* Define some needed abstract types. */
#ifndef gx_device_pdf_DEFINED
#  define gx_device_pdf_DEFINED
typedef struct gx_device_pdf_s gx_device_pdf;
#endif

/* ---------------- Structures ---------------- */

/* Define the abstract types if they aren't defined already (in gdevpdfx.h). */
#ifndef cos_types_DEFINED
#  define cos_types_DEFINED
typedef struct cos_object_s cos_object_t;
typedef struct cos_stream_s cos_stream_t;
typedef struct cos_dict_s cos_dict_t;
typedef struct cos_array_s cos_array_t;
typedef struct cos_value_s cos_value_t;
typedef struct cos_object_procs_s cos_object_procs_t;
typedef const cos_object_procs_t *cos_type_t;
#endif

/* Abstract types (defined concretely in gdevpdfo.c) */
typedef struct cos_element_s cos_element_t;
typedef struct cos_stream_piece_s cos_stream_piece_t;

/*
 * Define the object procedures for Cos objects.
 */
/*typedef struct cos_object_s cos_object_t;*/
/*typedef*/ struct cos_object_procs_s {

#define cos_proc_release(proc)\
  void proc(P2(cos_object_t *pco, client_name_t cname))
	cos_proc_release((*release));

#define cos_proc_write(proc)\
  int proc(P2(const cos_object_t *pco, gx_device_pdf *pdev))
	cos_proc_write((*write));

} /*cos_object_procs_t*/;
/*typedef const cos_object_procs_t *cos_type_t;*/
#define cos_type(pco) ((pco)->cos_procs)

/*
 * Define the generic structure for Cos objects.  Note that all Cos
 * objects have the same structure and type, aside from their elements.
 * This allows us to "mutate" a forward-referenced object into its
 * proper type in place.
 *
 * Note that we distinguish elements from contents.  Arrays and
 * dictionaries have only elements; streams have both elements
 * (additional elements of the stream dictionary) and contents pieces.
 *
 * The is_open member currently has no function other than error checking.
 * It is set to true when the object is created, to false by the CLOSE
 * and EP pdfmarks, and is checked by CLOSE, PUT, and SP.
 *
 * The is_graphics member exists only to ensure that the stream argument
 * of SP was created by BP/EP.
 *
 * The written member records whether the object has been written (copied)
 * into the contents or resource file.
 */
#define cos_object_struct(otype_s, etype)\
struct otype_s {\
    const cos_object_procs_t *cos_procs;	/* must be first */\
    long id;\
    etype *elements;\
    cos_stream_piece_t *pieces;\
    gx_device_pdf *pdev;\
    pdf_resource_t *pres;	/* only for BP/EP XObjects */\
    byte is_open;		/* see above */\
    byte is_graphics;		/* see above */\
    byte written;		/* see above */\
}
cos_object_struct(cos_object_s, cos_element_t);
#define private_st_cos_object()	/* in gdevpdfo.c */\
  gs_private_st_ptrs4(st_cos_object, cos_object_t, "cos_object_t",\
    cos_object_enum_ptrs, cos_object_reloc_ptrs, elements, pieces, pdev, pres)
extern const cos_object_procs_t cos_generic_procs;
#define cos_type_generic (&cos_generic_procs)

/*
 * Define the macro for casting any cos object to type cos_object_t.
 * Using cos_procs ensures that the argument is, in fact, a cos object.
 */
#define COS_OBJECT(pc) ((cos_object_t *)&((pc)->cos_procs))
#define CONST_COS_OBJECT(pc) ((const cos_object_t *)&((pc)->cos_procs))

/*
 * Define the structure for the value of an array or dictionary element.
 * This is where we create the union of composite and scalar types.
 */
/*typedef struct cos_value_s cos_value_t;*/
typedef enum {
    COS_VALUE_SCALAR = 0,	/* heap-allocated string */
    COS_VALUE_CONST,		/* shared (constant) string */
    COS_VALUE_OBJECT,		/* object referenced by # # R */
    COS_VALUE_RESOURCE		/* object referenced by /R# */
} cos_value_type_t;
struct cos_value_s {
    cos_value_type_t value_type;
    union vc_ {
	gs_string chars;	/* SCALAR, CONST */
	cos_object_t *object;	/* OBJECT, RESOURCE */
    } contents;
};
#define private_st_cos_value()	/* in gdevpdfo.c */\
  gs_private_st_composite(st_cos_value, cos_value_t,\
    "cos_value_t", cos_value_enum_ptrs, cos_value_reloc_ptrs)

/*
 * Define Cos arrays, dictionaries, and streams.
 *
 * The elements of arrays are stored sorted in decreasing index order.
 * The elements of dictionaries/streams are not sorted.
 * The contents pieces of streams are stored in reverse order.
 */
    /* array */
typedef struct cos_array_element_s cos_array_element_t;
cos_object_struct(cos_array_s, cos_array_element_t);
extern const cos_object_procs_t cos_array_procs;
#define cos_type_array (&cos_array_procs)
    /* dict */
typedef struct cos_dict_element_s cos_dict_element_t;
cos_object_struct(cos_dict_s, cos_dict_element_t);
extern const cos_object_procs_t cos_dict_procs;
#define cos_type_dict (&cos_dict_procs)
    /* stream */
cos_object_struct(cos_stream_s, cos_dict_element_t);
extern const cos_object_procs_t cos_stream_procs;
#define cos_type_stream (&cos_stream_procs)

/* ---------------- Procedures ---------------- */

/*
 * NOTE: Procedures that include "_c_" in their name do not copy their C
 * string argument(s).  To copy the argument, use the procedure that takes
 * a byte pointer and a length.
 */

/* Create a Cos object. */
cos_object_t *cos_object_alloc(P2(gx_device_pdf *, client_name_t));
cos_array_t *cos_array_alloc(P2(gx_device_pdf *, client_name_t));
cos_array_t *cos_array_from_floats(P4(gx_device_pdf *, const float *, uint,
				      client_name_t));
cos_dict_t *cos_dict_alloc(P2(gx_device_pdf *, client_name_t));
cos_stream_t *cos_stream_alloc(P2(gx_device_pdf *, client_name_t));

/* Get the allocator for a Cos object. */
gs_memory_t *cos_object_memory(P1(const cos_object_t *));
#define COS_OBJECT_MEMORY(pc) cos_object_memory(CONST_COS_OBJECT(pc))

/* Set the type of a generic Cos object. */
int cos_become(P2(cos_object_t *, cos_type_t));

/* Define wrappers for calling the object procedures. */
cos_proc_release(cos_release);
#define COS_RELEASE(pc, cname) cos_release(COS_OBJECT(pc), cname)
cos_proc_write(cos_write);
#define COS_WRITE(pc, pdev) cos_write(CONST_COS_OBJECT(pc), pdev)

/* Make a value to store into a composite object. */
const cos_value_t *cos_string_value(P3(cos_value_t *, const byte *, uint));
const cos_value_t *cos_c_string_value(P2(cos_value_t *, const char *));
const cos_value_t *cos_object_value(P2(cos_value_t *, cos_object_t *));
#define COS_OBJECT_VALUE(pcv, pc) cos_object_value(pcv, COS_OBJECT(pc))
/* A resource value is an object value referenced as /R#, not # # R. */
const cos_value_t *cos_resource_value(P2(cos_value_t *, cos_object_t *));
#define COS_RESOURCE_VALUE(pcv, pc) cos_resource_value(pcv, COS_OBJECT(pc))

/* Test whether a value is an object */
#define COS_VALUE_IS_OBJECT(pv) ((pv)->value_type >= COS_VALUE_OBJECT)

/*
 * Put an element in / add an element to a Cos object.  The _no_copy
 * procedures assume that the value, if not an object, is a newly allocated,
 * unshared string, allocated by the same allocator as the collection
 * itself, that should not be copied.
 */
    /* array */
int cos_array_put(P3(cos_array_t *, long, const cos_value_t *));
int cos_array_put_no_copy(P3(cos_array_t *, long, const cos_value_t *));
int cos_array_add(P2(cos_array_t *, const cos_value_t *));
int cos_array_add_no_copy(P2(cos_array_t *, const cos_value_t *));
int cos_array_add_c_string(P2(cos_array_t *, const char *));
int cos_array_add_int(P2(cos_array_t *, int));
int cos_array_add_real(P2(cos_array_t *, floatp));
int cos_array_add_object(P2(cos_array_t *, cos_object_t *));
    /* dict */
int cos_dict_put(P4(cos_dict_t *, const byte *, uint, const cos_value_t *));
int cos_dict_put_no_copy(P4(cos_dict_t *, const byte *, uint,
			    const cos_value_t *));
int cos_dict_put_c_key(P3(cos_dict_t *, const char *, const cos_value_t *));
int cos_dict_put_c_key_string(P4(cos_dict_t *, const char *, const byte *,
				 uint));
int cos_dict_put_c_key_int(P3(cos_dict_t *, const char *, int));
int cos_dict_put_c_key_real(P3(cos_dict_t *, const char *, floatp));
int cos_dict_put_c_key_floats(P4(cos_dict_t *, const char *, const float *, uint));
int cos_dict_put_c_key_object(P3(cos_dict_t *, const char *, cos_object_t *));
int cos_dict_put_string(P5(cos_dict_t *, const byte *, uint, const byte *,
			   uint));
int cos_dict_put_c_strings(P3(cos_dict_t *, const char *, const char *));
    /* stream */
int cos_stream_add(P2(cos_stream_t *, uint));
int cos_stream_add_since(P2(cos_stream_t *, long /*start_pos*/));
int cos_stream_add_bytes(P3(cos_stream_t *, const byte *, uint));
cos_dict_t *cos_stream_dict(P1(cos_stream_t *));

/* Get the first / next element for enumerating an array. */
const cos_array_element_t *
    cos_array_element_first(P1(const cos_array_t *));
const cos_array_element_t *
    cos_array_element_next(P3(const cos_array_element_t *, long *,
			      const cos_value_t **));

/*
 * Reorder the elements of an array for writing or after writing.  Usage:
 *	first_element = cos_array_reorder(pca, NULL);
 *	...
 *	cos_array_reorder(pca, first_element);
 */
cos_array_element_t *
    cos_array_reorder(P2(const cos_array_t *, cos_array_element_t *));

/* Look up a key in a dictionary. */
const cos_value_t *cos_dict_find(P3(const cos_dict_t *, const byte *, uint));
const cos_value_t *cos_dict_find_c_key(P2(const cos_dict_t *, const char *));

/* Set up a parameter list that writes into a Cos dictionary. */
typedef struct cos_param_list_writer_s {
    gs_param_list_common;
    cos_dict_t *pcd;
    int print_ok;
} cos_param_list_writer_t;
int cos_param_list_writer_init(P3(cos_param_list_writer_t *, cos_dict_t *,
				  int print_ok));

/* Create a stream that writes into a Cos stream. */
/* Closing the stream will free it. */
stream *cos_write_stream_alloc(P3(cos_stream_t *pcs, gx_device_pdf *pdev,
				  client_name_t cname));

/* Write a Cos value on the output. */
int cos_value_write(P2(const cos_value_t *, gx_device_pdf *));

/* Write the elements of a dictionary/stream on the output. */
int cos_dict_elements_write(P2(const cos_dict_t *, gx_device_pdf *));
int cos_stream_elements_write(P2(const cos_stream_t *, gx_device_pdf *)); /* = dict_elements_write */
int cos_stream_contents_write(P2(const cos_stream_t *, gx_device_pdf *));

/* Write/delete definitions of named objects. */
/* This is a special-purpose facility for pdf_close. */
int cos_dict_objects_write(P2(const cos_dict_t *, gx_device_pdf *));
int cos_dict_objects_delete(P1(cos_dict_t *));

/* Write a cos object as a PDF object. */
int cos_write_object(P2(cos_object_t *pco, gx_device_pdf *pdev));
#define COS_WRITE_OBJECT(pc, pdev) cos_write_object(COS_OBJECT(pc), pdev)

/* Free a Cos value owned by a Cos object. */
void cos_value_free(P3(const cos_value_t *, const cos_object_t *,
		       client_name_t));

/* Free a cos object. */
void cos_free(P2(cos_object_t *pco, client_name_t cname));
#define COS_FREE(pc, cname) cos_free(COS_OBJECT(pc), cname)

#endif /* gdevpdfo_INCLUDED */
