/* Copyright (C) 2002 CANON INC.

   Ghostscript printer driver
   for Canon CPCA printer devices (LIPS IV)

   Changed by Canon Inc.
   Date of change: 7 May 2002
*/

/* Copyright (C) 1998-2000 Norihito Ohmori.

   Ghostscript printer driver
   for Canon LBP (LIPS IV)

   This software is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
   to anyone for the consequences of using it or for whether it serves any
   particular purpose or works at all, unless he says so in writing.  Refer
   to the GNU General Public License for full details.

   Everyone is granted permission to copy, modify and redistribute
   this software, but only under the conditions described in the GNU
   General Public License.  A copy of this license is supposed to have been
   given to you along with this software so you can know your rights and
   responsibilities.  It should be in a file named COPYING.  Among other
   things, the copyright notice and this notice must be preserved on all
   copies.

*/




/*$Id: gdevl4v.c $ */
/* Vector Version of LIPS driver */


#include "math_.h"
#include "gx.h"
#include "gserrors.h"
#include "gsmatrix.h"
#include "gsparam.h"
#include "gxdevice.h"
#include "gscspace.h"
#include "gsutil.h"
#include "gdevvec.h"
#include "ghost.h"
#include "gzstate.h"
#include "imemory.h"
#include "igstate.h"
#include "cpca_gdevlips.h"
#include "cpca_mk.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* ---------------- Device definition ---------------- */

/* Device procedures */
private dev_proc_open_device(lips4v_open);
private dev_proc_output_page(lips4v_output_page);
private dev_proc_close_device(lips4v_close);
private dev_proc_copy_mono(lips4v_copy_mono);
private dev_proc_copy_color(lips4v_copy_color);

private dev_proc_put_params(lbp_common_put_params);
private dev_proc_get_params(lips4v_get_params);
private dev_proc_fill_mask(lips4v_fill_mask);
private dev_proc_begin_image(lips4v_begin_image);


#define X_DPI 600
#define Y_DPI 600

/* LIPS4Vのオプションパラメータ定義 */
typedef struct gx_device_lips4v_s
{
    gx_device_vector_common;
    lips_params_common;
    lips4_params_common;
    bool first_page;
    bool Duplex;
    int Duplex_set;
    bool OneBitMask;		/* for LIPS Bug */
    int ncomp;
    int MaskReverse;
    int MaskState;
    bool TextMode;
    int prev_x;
    int prev_y;
    gx_color_index prev_color;
    gx_color_index current_color;
    int linecap;
    /* for Font Downloading */
#define max_cached_chars 256	/* 128 * n */
    bool FontDL;
    int current_font;
    int count;
    gx_bitmap_id id_table[max_cached_chars + 1];
    gx_bitmap_id id_cache[max_cached_chars + 1];

    char    BindLoc[LIPS_BINDLOC_MAX];
    int     Gutter;
    bool    Collate;
    bool    Landscape;
    int     OutputBin;
    bool    Sort;
    bool    Stack;
    char    Staple[LIPS_STAPLE_MAX];
    bool    ModeH;
}
gx_device_lips4v;

gs_public_st_suffix_add0_final(st_device_lbp, gx_device_lips4v,
			       "gx_device_lips4v", device_lips4v_enum_ptrs,
			       device_lips4v_reloc_ptrs, gx_device_finalize,
			       st_device_vector);

#define lips_device_full_body(dtype, pprocs, dname, stype, w, h, xdpi, ydpi, ncomp, depth, mg, mc, dg, dc, lm, bm, rm, tm)\
        std_device_part1_(dtype, pprocs, dname, stype, open_init_closed),\
        dci_values(ncomp, depth, mg, mc, dg, dc),\
        std_device_part2_(w, h, xdpi, ydpi),\
        offset_margin_values(-lm * xdpi, -tm * ydpi, lm * 72.0, bm * 72.0, rm * 72.0, tm * 72.0),\
        std_device_part3_()


/* LBP-1310用にデバイスを作成 */
#define lbp1310_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1310",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)

/* LBP-1510用にデバイスを作成 */
#define lbp1510_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1510",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)

/* LBP-1610用にデバイスを作成 */
#define lbp1610_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1610",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)

/* LBP-1710用にデバイスを作成 */
#define lbp1710_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1710",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)

/* LBP-1810用にデバイスを作成 */
#define lbp1810_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1810",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)

/* LBP-1910用にデバイスを作成 */
#define lbp1910_device_body\
  lips_device_full_body(gx_device_lips4v, 0, "lbp1910",\
			&st_device_lbp,\
			DEFAULT_WIDTH_10THS * X_DPI / 10,\
			DEFAULT_HEIGHT_10THS * Y_DPI / 10,\
			X_DPI, Y_DPI,\
			1, 8, 1000, 1000, 5, 2,\
			LIPS4_LEFT_MARGIN_DEFAULT,\
			LIPS4_BOTTOM_MARGIN_DEFAULT,\
			LIPS4_RIGHT_MARGIN_DEFAULT,\
			LIPS4_TOP_MARGIN_DEFAULT)


/* 処理関数群の割り当て(gs処理でlips4vに依存する処理関数を定義する) */
#define lbp_common_procs\
	{	lips4v_open,\
		gx_upright_get_initial_matrix,\
		NULL,			/* sync_output */\
		lips4v_output_page,\
		lips4v_close,\
		gx_default_gray_map_rgb_color,\
		gx_default_gray_map_color_rgb,\
		gdev_vector_fill_rectangle,\
		NULL,			/* tile_rectangle */\
		lips4v_copy_mono,\
		lips4v_copy_color,\
		NULL,			/* draw_line */\
		NULL,			/* get_bits */\
		lips4v_get_params,\
		lbp_common_put_params,\
		NULL,			/* map_cmyk_color */\
		NULL,			/* get_xfont_procs */\
		NULL,			/* get_xfont_device */\
		NULL,			/* map_rgb_alpha_color */\
		gx_page_device_get_page_device,\
		NULL,			/* get_alpha_bits */\
		NULL,			/* copy_alpha */\
		NULL,			/* get_band */\
		NULL,			/* copy_rop */\
		gdev_vector_fill_path,\
		gdev_vector_stroke_path,\
		lips4v_fill_mask,\
		gdev_vector_fill_trapezoid,\
		gdev_vector_fill_parallelogram,\
		gdev_vector_fill_triangle,\
		NULL /****** WRONG ******/,	/* draw_thin_line */\
		lips4v_begin_image,\
		NULL,\
		NULL,\
		NULL,			/* strip_tile_rectangle */\
		NULL/******strip_copy_rop******/\
	}

#define common_params \
    lbp_common_procs,\
    vector_initial_values,\
    LIPS_CASSETFEED_DEFAULT,\
    LIPS_USERNAME_DEFAULT,\
    0 /* toner_density */ ,\
    FALSE /* toner_saving */ ,\
    0 /* toner_saving_set */ ,\
    0, 0, 0, 0, -1,\
    0 /* prev_duplex_mode */ ,\
    LIPS_NUP_DEFAULT,\
    LIPS_FACEUP_DEFAULT,\
    LIPS_MEDIATYPE_DEFAULT,\
    0 /* first_page */ ,\
    0 /* Duplex */ , 0 /* Duplex_set */ ,\
    0 /* OneBitMask */ ,\
    0 /* ncomp */ , -1 /* MaskReverse */ , 0 /* MaskState */ ,\
    0 /* TextMode */ , 0 /* prev_x */ , 0 /* prev_y */ ,\
    0 /* prev_color */ , 0 /* current_color */ ,\
    0 /* linecap */ ,\
    0 /* FontDL */ ,\
    -1 /* current_font */ ,\
    0 /* count */ ,\
    {},\
    {},\
    LIPS_BINDLOC_DEFAULT,       /* BindingLocation  */\
    LIPS_GUTTER_DEFAULT,        /* Gutter           */\
    LIPS_COLLATE_DEFAULT,       /* Collate          */\
    LIPS_LANDSCAPE_DEFAULT,     /* Landscape        */\
    LIPS_OUTBIN_DEFAULT,        /* OutputBin        */\
    LIPS_SORT_DEFAULT,          /* Sort             */\
    LIPS_STACK_DEFAULT,         /* Stack            */\
    LIPS_STAPLE_DEFAULT,        /* Staple           */\
    LIPS_MODEH_DEFAULT          /* SpecialModeH     */


/* Initialize Paramater */
/* 初期値設定はここで行う(各デバイス毎に初期値を設定しておく) */
/* LBP-1310用 */
gx_device_lips4v far_data gs_lbp1310_device = {
    lbp1310_device_body,
    common_params 
};

/* LBP-1510用 */
gx_device_lips4v far_data gs_lbp1510_device = {
    lbp1510_device_body,
    common_params
};
/* LBP-1610用 */
gx_device_lips4v far_data gs_lbp1610_device = {
    lbp1610_device_body,
    common_params
};
/* LBP-1710用 */
gx_device_lips4v far_data gs_lbp1710_device = {
    lbp1710_device_body,
    common_params
};
/* LBP-1810用 */
gx_device_lips4v far_data gs_lbp1810_device = {
    lbp1810_device_body,
    common_params
};

/* LBP-1910用(P560UG) */
gx_device_lips4v far_data gs_lbp1910_device = {
    lbp1910_device_body,
    common_params
};

/* Vector device implementation */
private int lips4v_beginpage(P1(gx_device_vector * vdev));
private int lips4v_setfillcolor(P2(gx_device_vector * vdev,
				   const gx_drawing_color * pdc));
private int lips4v_setstrokecolor(P2(gx_device_vector * vdev,
				     const gx_drawing_color * pdc));
private int lips4v_setdash(P4(gx_device_vector * vdev, const float *pattern,
			      uint count, floatp offset));
private int lips4v_setflat(P2(gx_device_vector * vdev, floatp flatness));
private int
lips4v_setlogop(P3

		(gx_device_vector * vdev, gs_logical_operation_t lop,
		 gs_logical_operation_t diff));
private int

lips4v_beginpath(P2(gx_device_vector * vdev, gx_path_type_t type));
private int
lips4v_moveto(P6
	      (gx_device_vector * vdev, floatp x0, floatp y0, floatp x,
	       floatp y, gx_path_type_t type));
private int
lips4v_lineto(P6
	      (gx_device_vector * vdev, floatp x0, floatp y0, floatp x,
	       floatp y, gx_path_type_t type));
private int
lips4v_curveto(P10
	       (gx_device_vector * vdev, floatp x0, floatp y0, floatp x1,
		floatp y1, floatp x2, floatp y2, floatp x3, floatp y3,
		gx_path_type_t type));
private int
lips4v_closepath(P6
		 (gx_device_vector * vdev, floatp x, floatp y, floatp x_start,
		  floatp y_start, gx_path_type_t type));

private int lips4v_endpath(P2(gx_device_vector * vdev, gx_path_type_t type));
private int lips4v_setlinewidth(gx_device_vector * vdev, floatp width);
private int lips4v_setlinecap(gx_device_vector * vdev, gs_line_cap cap);
private int lips4v_setlinejoin(gx_device_vector * vdev, gs_line_join join);
private int lips4v_setmiterlimit(gx_device_vector * vdev, floatp limit);
private const gx_device_vector_procs lips4v_vector_procs = {
    /* Page management */
    lips4v_beginpage,
    /* Imager state */
    lips4v_setlinewidth,
    lips4v_setlinecap,
    lips4v_setlinejoin,
    lips4v_setmiterlimit,
    lips4v_setdash,
    lips4v_setflat,
    lips4v_setlogop,
    /* Other state */
    lips4v_setfillcolor,	/* fill & stroke colors are the same */
    lips4v_setstrokecolor,
    /* Paths */
    gdev_vector_dopath,
    gdev_vector_dorect,
    lips4v_beginpath,
    lips4v_moveto,
    lips4v_lineto,
    lips4v_curveto,
    lips4v_closepath,
    lips4v_endpath
};

/* ---------------- File header ---------------- */
private const char *l4v_file_header4 = "\033%@\033P41;";

private const char *l4vmono_file_header =

    ";1J" L4VMONO_STRING LIPS_VERSION "\033\\\033[0\"p\033<";

private const char *l4vcolor_file_header =

    ";1J" L4VCOLOR_STRING LIPS_VERSION "\033\\\033[1\"p\033<";

private const char *page_header = "\033[0&}#" LIPS_IS2;

private const char *l4vmono_page_header = "!13" LIPS_IS2 "$" LIPS_IS2;

private const char *l4vcolor_page_header = "!11" LIPS_IS2 "$" LIPS_IS2;

/* ---------------- Utilities ---------------- */
char *spool_buf;
char *cpca_buf;
static RasterArg_t cpca_arg;

#define MAX_DATA_SPOOL_SIZE   0x2800 /* 10 KB */
#define MAX_CPCA_DATA_SIZE    MAX_DATA_SPOOL_SIZE + 256 /* data + cpca */

private int
out_cpcaStartPacket(stream *stream_buf)
{
    uint used;
	int status = 0;
	uint cpca_size = 0;

	/* make cpca packet */
	cpca_size = cpca_JobStart(cpca_buf, &cpca_arg);
	/* gs stream out */
	sputs(stream_buf, (const byte *)cpca_buf, cpca_size, &used);

	return(status);
}

private int
out_cpcaEndPacket(FILE *file, PRasterArg_t arg)
{
	int status = 0;
	uint cpca_size = 0;
	char isCancel = 0x00;	/* 正常終了 */

	if (arg->StateFlg == 2) {
		/* make cpca packet (LIPS JOB END) */
	    sprintf(spool_buf, LIPS_DCS "0J" LIPS_ST);
		cpca_size = cpca_SendData(strlen(spool_buf), spool_buf, cpca_buf, 0);
		/* gs file out */
		fwrite(cpca_buf, cpca_size, 1, file);

		/* make cpca packet (CPCA JOB END) */
		cpca_size = cpca_JobEnd(cpca_buf, arg, isCancel);
		/* gs file out */
		fwrite(cpca_buf, cpca_size, 1, file);
	} else {
		*cpca_buf = 0x00;
		fwrite(cpca_buf, 1, 1, file);
	}
	return(status);
}

private int
spool_sputs(stream *stream_buf, const char *str, uint len, int contFlag)
{
	int status = 0;
	uint cpca_size = 0;
	static int cpca_count;
	static int total_count;

	if (contFlag != 0)
	{
		if ((cpca_count + len) > (MAX_DATA_SPOOL_SIZE))
		{
		    uint used;
			/* make cpca packet */
			cpca_size = cpca_SendData(cpca_count, spool_buf, cpca_buf, 1);
			/* gs stream out */
		    sputs(stream_buf, (const byte *)cpca_buf, cpca_size, &used);
			/* reset cpca_count */
			memcpy(spool_buf, str, len);
			cpca_count = len;
			total_count += len;

		} else {
			/* copy spool buffer ( MAX_DATA_SPOOL_SIZE < data size ) */
			memcpy(spool_buf + cpca_count, str, len);
			cpca_count += len;
			total_count += len;
		}
	} else {
	    uint used;
		/* make cpca packet */
		cpca_size = cpca_SendData(cpca_count, spool_buf, cpca_buf, 1);
		/* gs stream out */
	    sputs(stream_buf, (const byte *)cpca_buf, cpca_size, &used);
		/* reset count */
		cpca_count = 0;
		total_count = 0;
	}

	return status;
}

private int
spool_sputc(stream *stream_buf, uint value, uint len, int contFlag)
{
	char	char_code;
	int		status = 0;

	char_code = (char)value;
	status = spool_sputs(stream_buf, &char_code, len, contFlag);
	return status;
}

private void
lips_param(int param, char *c)
{
    int i, j;
    bool bSign;

    bSign = TRUE;
    if (param < 0) {
	bSign = FALSE;
	param = -param;
    }
    if (param < 16)
	i = 1;
    else if (param < 1024)
	i = 2;
    else if (param < 65536)
	i = 3;
    else
	i = 4;

    c[i] = '\0';
    c[i - 1] = (param & 0x0f) | 0x20 | (bSign ? 0x10 : 0x00);
    param >>= 4;
    for (j = i - 2; j >= 0; j--) {
	c[j] = (param & 0x3f) | 0x40;
	param >>= 6;
    }
}

private void
sput_lips_int(stream * s, int param)
{
    char c[5];

    lips_param(param, c);

	spool_sputs( s, c, strlen(c), 1 ) ;
}

/* Put a string on a stream.
   This function is copy of `pputs' in gdevpstr.c */
private int
lputs(stream * s, const char *str)
{
    uint len = strlen(str);
	spool_sputs( s, str, len, 1 ) ;

    return 0;
}

/* Write a string on a stream. */
private void
put_bytes(stream * s, const byte * data, uint count)
{
	spool_sputs( s, data, count, 1 ) ;
}

/* for Font Downloading */
private void
put_int(stream * s, uint number)
{
	spool_sputc( s, number >> 8, 1, 1 ) ;
	spool_sputc( s, number & 0xff, 1, 1 ) ;
}

private int
lips4v_range_check(gx_device * dev)
{
    int width = dev->MediaSize[0];
    int height = dev->MediaSize[1];
    int xdpi = dev->x_pixels_per_inch;
    int ydpi = dev->y_pixels_per_inch;
    int copies = dev->NumCopies;
    const char *dname = dev->dname;
    int     device_id = 0;
    int paper_size;

    /* device search */
    if (strcmp(dname, "lbp1310") == 0) {
        device_id = LBP1310;
    } else if (strcmp(dname, "lbp1510") == 0) {
        device_id = LBP1510;
    } else if (strcmp(dname, "lbp1610") == 0) {
        device_id = LBP1610;
    } else if (strcmp(dname, "lbp1710") == 0) {
        device_id = LBP1710;
    } else if (strcmp(dname, "lbp1810") == 0) {
        device_id = LBP1810;
    } else if (strcmp(dname, "lbp1910") == 0) {
        device_id = LBP1910;
    } else {
        return_error(gs_error_rangecheck);
    }

    /* Paper Size Check */
    paper_size = lips4v_media_selection(width, height);

    if(paper_size >= 80) {
        return_error(gs_error_rangecheck);
    } else if ((device_id == LBP1310 && (paper_size == A3_SIZE ||
                                         paper_size == B4_SIZE ||
                                         paper_size == ENVYOU2_SIZE ||
                                         paper_size == ENVKAKU2_SIZE ||
                                         paper_size == LEDGER_SIZE )) ||    /* LBP1310 */
               (device_id == LBP1510 && paper_size == ENVKAKU2_SIZE ) ||    /* LBP1510 */
               (device_id == LBP1610 && (paper_size == ENVYOU2_SIZE ||
                                         paper_size == ENVKAKU2_SIZE )) ||  /* LBP1610 */
               (device_id == LBP1710 && paper_size == ENVKAKU2_SIZE ) ||    /* LBP1710 */
               (device_id == LBP1810 && (paper_size == ENVYOU2_SIZE ||
                                         paper_size == ENVKAKU2_SIZE )) ||  /* LBP1810 */
               (device_id == LBP1910 && (paper_size == POSTCARD_SIZE ||
                                         paper_size == DBL_POSTCARD_SIZE ||
                                         paper_size == ENVYOU2_SIZE ))) {  /* LBP1910 */
        return_error(gs_error_rangecheck);
    }
    cpca_arg.PaperNum = paper_size;


    /* Resolution Check */
    if (xdpi != ydpi)
    return_error(gs_error_rangecheck);
    else {
    if (xdpi != LIPS4_DPI_QUICK && xdpi != LIPS4_DPI_FINE && xdpi != LIPS4_DPI_SUPERFINE) {
            return_error(gs_error_rangecheck);
        } else if ((device_id == LBP1510 && xdpi == LIPS4_DPI_SUPERFINE) ||
                   (device_id == LBP1710 && xdpi == LIPS4_DPI_SUPERFINE) ||
                   (device_id == LBP1910 && xdpi == LIPS4_DPI_SUPERFINE)) {
            return_error(gs_error_rangecheck);
        }
    }

    /* Copies Check */
    if (copies < CANON_LIPS4_MIN_COPIES || copies > CANON_LIPS4_MAX_COPIES) {
        return_error(gs_error_rangecheck);
    }

    return 0;
}


private void
lips4v_set_cap(gx_device * dev, int x, int y)
{

    char cap[15];
    stream *s = gdev_vector_stream((gx_device_vector *) dev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    int dx = x - pdev->prev_x;
    int dy = y - pdev->prev_y;


    if (dx > 0) {
	sprintf(cap, LIPS_CSI "%da", dx);
	lputs(s, cap);
    } else if (dx < 0) {
	sprintf(cap, LIPS_CSI "%dj", -dx);
	lputs(s, cap);
    }
    if (dy > 0) {
	sprintf(cap, LIPS_CSI "%dk", dy);
	lputs(s, cap);
    } else if (dy < 0) {
	sprintf(cap, LIPS_CSI "%de", -dy);
	lputs(s, cap);
    }
    pdev->prev_x = x;
    pdev->prev_y = y;
}

#define POINT 18

/* Font Downloading Routine */
private int
lips4v_copy_text_char(gx_device * dev, const byte * data,
		      int raster, gx_bitmap_id id, int x, int y, int w, int h)
{
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    stream *s = gdev_vector_stream((gx_device_vector *) dev);
    uint width_bytes = (w + 7) >> 3;
    uint size = width_bytes * h;
    int i, j;
    uint ccode = 0;
    char cset_sub[9], cset[64], cset_number[8], text_color[15];
    int cell_length = (POINT * (int)dev->x_pixels_per_inch) / 72;
    bool download = TRUE;

    if (w > cell_length || h > cell_length || !pdev->FontDL)
	return -1;

    for (j = pdev->count - 1; j >= 0; j--) {
	if (pdev->id_table[j] == id)
	    /* font is found */
	{
	    download = FALSE;
	    ccode = j;
	    for (i = j; i < pdev->count - 1; i++) {
		pdev->id_cache[i] = pdev->id_cache[i + 1];
	    }
	    pdev->id_cache[pdev->count - 1] = id;
	    break;
	}
    }

    if (download) {
	if (pdev->count > max_cached_chars - 1) {
	    gx_bitmap_id tmpid = pdev->id_cache[0];

	    for (j = pdev->count - 1; j >= 0; j--) {
		if (pdev->id_table[j] == tmpid) {
		    ccode = j;
		    break;
		}
	    }
	    for (i = j; i < pdev->count - 1; i++) {
		pdev->id_cache[i] = pdev->id_cache[i + 1];
	    }
	    pdev->id_cache[pdev->count - 1] = tmpid;
	} else {
	    ccode = pdev->count;
	    pdev->id_cache[pdev->count] = id;
	}
    }
    if (pdev->TextMode == FALSE) {
	/* Text mode */
	lputs(s, "}p");
	sput_lips_int(s, x);
	sput_lips_int(s, y);
	lputs(s, LIPS_IS2);
	pdev->TextMode = TRUE;
	pdev->prev_x = x;
	pdev->prev_y = y;
    } else
	lips4v_set_cap(dev, x, y);

    if (download) {
	if (ccode % 128 == 0 && ccode == pdev->count) {
	    /* 文字セット登録補助命令 */
	    sprintf(cset_sub, LIPS_DCS "%dx" LIPS_ST, ccode / 128);
	    lputs(s, cset_sub);
	    /* 文字セット登録命令 */
	    sprintf(cset,
		    LIPS_CSI
		    "%d;1;0;0;3840;8;400;100;0;0;200;%d;%d;0;0;;;;;%d.p",
		    size + 9, cell_length,	/* Cell Width */
		    cell_length,	/* Cell Height */
		    (int)dev->x_pixels_per_inch);
	    lputs(s, cset);
	} else {
	    /* 1文字登録命令 */
	    sprintf(cset,
		    LIPS_CSI "%d;%d;8;%d.q",
		    size + 9, ccode / 128, (int)dev->x_pixels_per_inch);
	    lputs(s, cset);
	}

	/* ユーザ文字登録データ のヘッダ */
	spool_sputc(s, ccode % 128, 1, 1);
	put_int(s, w);
	put_int(s, 0);
	put_int(s, h);
	put_int(s, 0);
	for (i = h - 1; i >= 0; --i) {
	    put_bytes(s, data + i * raster, width_bytes);
	}
    }
    /* 文字セット・アサイン番号選択命令2 */
    if (download) {
	if (pdev->current_font != ccode / 128) {
	    sprintf(cset_number, LIPS_CSI "%d%%v", ccode / 128);
	    lputs(s, cset_number);
	    pdev->current_font = ccode / 128;
	}
    } else {
	if (pdev->current_font != ccode / 128) {
	    sprintf(cset_number, LIPS_CSI "%d%%v", ccode / 128);
	    lputs(s, cset_number);
	    pdev->current_font = ccode / 128;
	}
    }

    /* カラー */
    if (pdev->current_color != pdev->prev_color) {
	if (pdev->color_info.depth == 8) {
	    lputs(s, LIPS_CSI "?10;2;");
	    sprintf(text_color, "%d",
		    (int)(pdev->color_info.max_gray - pdev->current_color));
	} else {
	    int r = (pdev->current_color >> 16) * 1000.0 / 255.0;
	    int g = ((pdev->current_color >> 8) & 0xff) * 1000.0 / 255.0;
	    int b = (pdev->current_color & 0xff) * 1000.0 / 255.0;

	    lputs(s, LIPS_CSI "?10;;");
	    sprintf(text_color, "%d;%d;%d", r, g, b);
	}
	lputs(s, text_color);
	lputs(s, "%p");
	pdev->prev_color = pdev->current_color;
    }
    /* 制御文字印字命令 */
    if (ccode % 128 == '\x00' ||
	(ccode % 128 >= '\x07' && ccode % 128 <= '\x0F') ||
	ccode % 128 == '\x1B') lputs(s, LIPS_CSI "1.v");
	spool_sputc(s, ccode % 128, 1, 1);

    if (download) {
	pdev->id_table[ccode] = id;
	if (pdev->count < max_cached_chars - 1)
	    pdev->count++;
    }
    return 0;
}

private void
reverse_buffer(byte * buf, int Len)
{
    int i;

    for (i = 0; i < Len; i++)
	*(buf + i) = ~*(buf + i);
}

private void
lips4v_write_image_data(gx_device_vector * vdev, byte * buf, int tbyte,
			int reverse)
{
    stream *s = gdev_vector_stream(vdev);
    byte *cbuf = gs_alloc_bytes(vdev->memory, tbyte * 3 / 2,
				"lips4v_write_image_data(cbuf)");
    byte *cbuf_rle = gs_alloc_bytes(vdev->memory, tbyte * 3,
				    "lips4v_write_image_data(cbuf_rle)");
    int Len, Len_rle;

    if (reverse)
	reverse_buffer(buf, tbyte);


    Len = lips4v_packbits_encode(buf, cbuf, tbyte);
    Len_rle = lips4v_rle_encode(buf, cbuf_rle, tbyte);

    if (Len > tbyte && Len_rle > tbyte) {
	/* Not compress */
	lputs(s, "0");
	sput_lips_int(s, tbyte);
	lputs(s, LIPS_IS2);

	put_bytes(s, buf, tbyte);
    } else if (Len > Len_rle) {
	/* Use RunLength encode */
	lputs(s, ":");
	sput_lips_int(s, Len_rle);
	lputs(s, LIPS_IS2);

	put_bytes(s, cbuf_rle, Len_rle);
    } else {
	/* Use PackBits encode */
	lputs(s, ";");
	sput_lips_int(s, Len);
	lputs(s, LIPS_IS2);

	put_bytes(s, cbuf, Len);
    }

    gs_free_object(vdev->memory, cbuf, "lips4v_write_image_data(cbuf)");
    gs_free_object(vdev->memory, cbuf_rle,
		   "lips4v_write_image_data(cbuf_rle)");
}


/* ---------------- Vector device implementation ---------------- */

private int
lips4v_beginpage(gx_device_vector * vdev)
{				/*
				 * We can't use gdev_vector_stream here, because this may be called
				 * from there before in_page is set.
				 */
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
    stream *s = vdev->strm;
    int dpi = vdev->x_pixels_per_inch;
    int width = pdev->MediaSize[0];
    int height = pdev->MediaSize[1];
    int paper_size, x0, y0;
    char unit[12];
    char nup_char[10];
    char paper[16];
	int NumCopies = pdev->NumCopies;

   /* ベクタ・モード移行命令 CSI &} は頁ごとに発行する */

    if (pdev->first_page) {
		/* set resolution */
		cpca_arg.Resolution = dpi;
		/* set copy number */
		cpca_arg.Copies = NumCopies;
		out_cpcaStartPacket(s);
		/* set cpca flag */
		cpca_arg.StateFlg = 2;

		lputs(s, l4v_file_header4);

		if (pdev->color_info.depth == 8)
	    	lputs(s, l4vmono_file_header);
		else
	    	lputs(s, l4vcolor_file_header);
    }

	/* 用紙種類 */
    if (strcmp(pdev->mediaType, "PlainPaper") == 0)
	lputs(s, LIPS_CSI "20\'t");
    else if (strcmp(pdev->mediaType, "PlainPaperL") == 0 )
	lputs(s, LIPS_CSI "15\'t");
    else if (strcmp(pdev->mediaType, "CardBoard") == 0 )
	lputs(s, LIPS_CSI "30\'t");
    else if (strcmp(pdev->mediaType, "CardBoardH") == 0 )
	lputs(s, LIPS_CSI "35\'t");
    else if (strcmp(pdev->mediaType, "OHP") == 0)
	lputs(s, LIPS_CSI "40\'t");

    /* 用紙サイズ */
	paper_size = cpca_arg.PaperNum;
    paper_size += pdev->Landscape;

    sprintf(paper, LIPS_CSI "%dp", paper_size);
    lputs(s, paper);

    pdev->prev_paper_size = paper_size;
    pdev->prev_paper_width = width;
    pdev->prev_paper_height = height;

    /* N-up Printing Setting */
    if (pdev->first_page) {
		if (pdev->nup != 1) {
		    sprintf(nup_char, LIPS_CSI "%d1;;%do", pdev->nup, paper_size);
		    lputs(s, nup_char);
		}
    }

    lputs(s, LIPS_CSI "?1;4;5;6;14l");
    lputs(s, LIPS_CSI "?2;3;h");

    /* size unit (dpi) */
    lputs(s, LIPS_CSI "11h");
    sprintf(unit, LIPS_CSI "?7;%d I", (int)pdev->x_pixels_per_inch);
    lputs(s, unit);
    lputs(s, page_header);	/* vector mode */

    lputs(s, "!0");		/* size unit (dpi) */
    sput_lips_int(s, dpi);
    lputs(s, "1" LIPS_IS2);

    if (pdev->color_info.depth == 8)
	lputs(s, l4vmono_page_header);
    else
	lputs(s, l4vcolor_page_header);

    lputs(s, "(00");
    sput_lips_int(s,
		  ((width - dev_l_margin(vdev) - dev_r_margin(vdev)) * dpi) /
		  72);
    sput_lips_int(s,
		  ((height - dev_b_margin(vdev) - dev_t_margin(vdev)) * dpi) /
		  72);
    lputs(s, LIPS_IS2);

    /* 原点移動命令 */
    x0 = (dev_l_margin(vdev) - 5. / MMETER_PER_INCH) * dpi;
    y0 = (dev_b_margin(vdev) - 5. / MMETER_PER_INCH) * dpi;

    if (x0 != 0 && y0 != 0) {
	lputs(s, "}\"");
	sput_lips_int(s, x0);
	sput_lips_int(s, y0);
	lputs(s, LIPS_IS2);
    }
    lputs(s, "I00" LIPS_IS2 "}F2" LIPS_IS2);
    /* lputs(s, LIPS_IS2 "J1" LIPS_IS2); */
    lputs(s, "}H1" LIPS_IS2 "*0" LIPS_IS2);

    pdev->MaskState = 1;	/* 初期化: 透過 */
    pdev->linecap = 0;
    lputs(s, "}M");
    sput_lips_int(s, 3277);	/* 11 degree : 16383 * 2 / 10 */
    lputs(s, LIPS_IS2);
    lputs(s, "}I1" LIPS_IS2);	/* non-zero winding rule is default */

    return 0;
}


private int
lips4v_setlinewidth(gx_device_vector * vdev, floatp width)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;


    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    if (width < 1)
	width = 1;

    lputs(s, "F1");
    sput_lips_int(s, width);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_setlinecap(gx_device_vector * vdev, gs_line_cap cap)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
    char c[6];
    int line_cap = 0;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    switch (cap) {
	case 0:
	case 3:
	line_cap = 0;		/* butt */
	break;
	case 1:
	line_cap = 1;		/* round */
	break;
	case 2:
	line_cap = 2;		/* square */
	break;
    }
    /* 線端形状指定命令 */
    sprintf(c, "}E%d" LIPS_IS2, line_cap);
    lputs(s, c);

    pdev->linecap = cap;

    return 0;
}

private int
lips4v_setlinejoin(gx_device_vector * vdev, gs_line_join join)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;

/* 線接続指定命令 */
    char c[5];
    int lips_join = 0;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }

    switch (join) {
	case 0:
	lips_join = 2;		/* miter */
	break;
	case 1:
	lips_join = 1;		/* round */
	break;
	case 2:
	lips_join = 3;		/* bevel */
	break;
	case 3:
	case 4:
	lips_join = 0;		/* none */
	break;
    }

    sprintf(c, "}F%d" LIPS_IS2, lips_join);
    lputs(s, c);

    return 0;
}

private int
lips4v_setmiterlimit(gx_device_vector * vdev, floatp limit)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
    floatp lips_miterlimit;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    lips_miterlimit = (16383.0 * 2.0) / limit;

    lputs(s, "}M");
    sput_lips_int(s, lips_miterlimit);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_setfillcolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
{

    if (!gx_dc_is_pure(pdc))
	return_error(gs_error_rangecheck);
    {
	stream *s = gdev_vector_stream(vdev);
	gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
	gx_color_index color = gx_dc_pure_color(pdc);
	int drawing_color = 0;
	float r = 0, g = 0, b = 0;

	if (vdev->color_info.depth == 8) {
	    drawing_color = vdev->color_info.max_gray - color;
	} else {
	    r = (color >> 16) * 1000.0 / 255.0;
	    g = ((color >> 8) & 0xff) * 1000.0 / 255.0;
	    b = (color & 0xff) * 1000.0 / 255.0;
	}

	if (pdev->TextMode) {
	    lputs(s, LIPS_CSI "&}");
	    pdev->TextMode = FALSE;
	}
	pdev->current_color = color;

	if (color == gx_no_color_index)
	    lputs(s, "I0" LIPS_IS2);
	else
	    lputs(s, "I1" LIPS_IS2);

	/* 塗りつぶしカラー指定命令 */
	/* J {color} IS2 */
	lputs(s, "J");
	if (vdev->color_info.depth == 8) {
	    sput_lips_int(s, drawing_color);
	} else {
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	}
	lputs(s, LIPS_IS2);

	/* 単色イメージ・カラー指定命令 */
	/* }T {color} IS2 */
	lputs(s, "}T");
	if (vdev->color_info.depth == 8) {
	    sput_lips_int(s, drawing_color);
	} else {
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	}
	lputs(s, LIPS_IS2);
    }
    return 0;
}

private int
lips4v_setstrokecolor(gx_device_vector * vdev, const gx_drawing_color * pdc)
{
    if (!gx_dc_is_pure(pdc))
	return_error(gs_error_rangecheck);
    {
	stream *s = gdev_vector_stream(vdev);
	gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
	gx_color_index color = gx_dc_pure_color(pdc);
	float r = 0, g = 0, b = 0;

	if (vdev->color_info.depth == 24) {
	    r = (color >> 16) * 1000 / 255.0;
	    g = ((color >> 8) & 0xff) * 1000 / 255.0;
	    b = (color & 0xff) * 1000 / 255.0;
	}

	if (pdev->TextMode) {
	    lputs(s, LIPS_CSI "&}");
	    pdev->TextMode = FALSE;
	}
	/* ラインカラー指定命令 */
	/* G {color} IS2 */
	lputs(s, "G");
	if (vdev->color_info.depth == 8) {
	    sput_lips_int(s, vdev->color_info.max_gray - color);
	} else {
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	}
	lputs(s, LIPS_IS2);
    }
    return 0;
}

/* 線種指定命令 */
private int
lips4v_setdash(gx_device_vector * vdev, const float *pattern, uint count,
	       floatp offset)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;
    int i;
    float scale, xscale, yscale;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }

    if (count == 0) {
	lputs(s, "E10" LIPS_IS2);
    } else {
	lputs(s, "}d\x2C" "1");
	sput_lips_int(s, offset);

	for (i = 0; i < count; ++i) {
	    if (pdev->linecap == 1 && count == 2 && pattern[0] == 0) {
		if (i == 0) {
		    sput_lips_int(s, 1);
		} else {
		    sput_lips_int(s, pattern[i] - 1);
		}
	    } else {
		sput_lips_int(s, pattern[i]);
	    }
	}
	lputs(s, LIPS_IS2 "E1\x2C" "0" LIPS_IS2);
    }

    return 0;
}

/* パス平滑度指定 */
private int
lips4v_setflat(gx_device_vector * vdev, floatp flatness)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    lputs(s, "Pf");
    sput_lips_int(s, flatness);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_setlogop(gx_device_vector * vdev, gs_logical_operation_t lop,
		gs_logical_operation_t diff)
{
/****** SHOULD AT LEAST DETECT SET-0 & SET-1 ******/
    return 0;
}

private int
lips4v_beginpath(gx_device_vector * vdev, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    /* パス構築開始命令 */
    if (type & gx_path_type_clip) {
	lputs(s, "P(10" LIPS_IS2);
    } else
	lputs(s, "P(00" LIPS_IS2);

    return 0;
}

private int
lips4v_moveto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x,
	      floatp y, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);

    /* サブパス開始命令 p1 */
    lputs(s, "p10");
    sput_lips_int(s, x);
    sput_lips_int(s, y);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_lineto(gx_device_vector * vdev, floatp x0, floatp y0, floatp x,
	      floatp y, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);
    gx_device_lips4v *const pdev = (gx_device_lips4v *) vdev;

    /* if round cap */
    if (pdev->linecap == 1) {
	if ((x0 == x) && (y0 == y))
	    x += 1;
    }

    /* パス・ポリライン命令 */
    lputs(s, "p402");
    sput_lips_int(s, x);
    sput_lips_int(s, y);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_curveto(gx_device_vector * vdev, floatp x0, floatp y0,
	       floatp x1, floatp y1, floatp x2, floatp y2, floatp x3,
	       floatp y3, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);

    /* パス・ポリライン命令 */
    lputs(s, "p404");
    sput_lips_int(s, x1);
    sput_lips_int(s, y1);
    sput_lips_int(s, x2);
    sput_lips_int(s, y2);
    sput_lips_int(s, x3);
    sput_lips_int(s, y3);
    lputs(s, LIPS_IS2);

    return 0;
}

private int
lips4v_closepath(gx_device_vector * vdev, floatp x, floatp y,
		 floatp x_start, floatp y_start, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);

    lputs(s, "p0" LIPS_IS2);
    return 0;
}

private int
lips4v_endpath(gx_device_vector * vdev, gx_path_type_t type)
{
    stream *s = gdev_vector_stream(vdev);

    lputs(s, "P)" LIPS_IS2);
    if (type & gx_path_type_rule) {
	if (type & gx_path_type_winding_number) {
	    lputs(s, "}I1" LIPS_IS2);
	} else {
	    lputs(s, "}I0" LIPS_IS2);
	}
    }
    if (type & gx_path_type_fill) {
	if (type & gx_path_type_stroke) {
	    lputs(s, "P&00" LIPS_IS2);
	} else {
	    lputs(s, "PF00" LIPS_IS2);
	}
    }
    if (type & gx_path_type_stroke) {
	lputs(s, "PS00" LIPS_IS2);
    }
    if (type & gx_path_type_clip) {
	lputs(s, "PC10" LIPS_IS2);
    }
    return 0;
}

/* ---------------- Driver procedures ---------------- */

/* ------ Open/close/page ------ */

/* Open the device. */
private int
lips4v_open(gx_device * dev)
{
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;

    int code;

    code = lips4v_range_check(dev);
    if (code < 0)
	return code;

    vdev->v_memory = dev->memory;
/****** WRONG ******/
    vdev->vec_procs = &lips4v_vector_procs;


    code = gdev_vector_open_file_bbox(vdev, 512, true);
    if (code < 0)
	return code;

    gdev_vector_init(vdev);
    pdev->first_page = true;

    /* alloc spool buffer memory */
    spool_buf = (char *)malloc(MAX_DATA_SPOOL_SIZE);
    /* alloc cpca buffer memory */
    cpca_buf = (char *)malloc(MAX_CPCA_DATA_SIZE);


    return 0;
}

/* Wrap up ("output") a page. */
private int
lips4v_output_page(gx_device * dev, int num_copies, int flush)
{
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    stream *s = gdev_vector_stream(vdev);

    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    lputs(s, "%" LIPS_IS2 "}p" LIPS_IS2);

	pdev->prev_num_copies = num_copies;
    lputs(s, LIPS_FF);

	/* spool_end_flag */
	cpca_arg.PageCount++;
	if(cpca_arg.PageCount % pdev->nup)
	{
		cpca_arg.ImpressVal = cpca_arg.PageCount / pdev->nup + 1;
	} else {
		cpca_arg.ImpressVal = cpca_arg.PageCount / pdev->nup;
	}
	spool_sputs(s, NULL, 0, 0);

    sflush(s);
    vdev->in_page = false;
    pdev->first_page = false;
    gdev_vector_reset(vdev);


    return 0;
}

private int
lips4v_close(gx_device * dev)
{
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    FILE *f = vdev->file;

    /* cpca end packet */
    out_cpcaEndPacket(f, &cpca_arg);
	/* free spool buffer */
	free(spool_buf);
	/* free cpca buffer */
	free(cpca_buf);

    gdev_vector_close_file(vdev);

    return 0;
}

/* Close the device. */
/* Note that if this is being called as a result of finalization, */
/* the stream may no longer exist; but the file will still be open. */

/* ---------------- Get/put parameters ---------------- */

/* Get parameters. */
private int
lips4v_get_params(gx_device * dev, gs_param_list * plist)
{
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    int code = gdev_vector_get_params(dev, plist);
    int ncode;
    gs_param_string usern;
    gs_param_string pmedia;

    gs_param_string pbindloc;
    gs_param_string pstaple;

    if (code < 0)
	return code;

    if ((ncode = param_write_int(plist, LIPS_OPTION_CASSETFEED,
				 &pdev->cassetFeed)) < 0)
	code = ncode;

    if ((ncode = param_write_int(plist, LIPS_OPTION_NUP, &pdev->nup)) < 0)
	code = ncode;

    if ((ncode = param_write_int(plist, LIPS_OPTION_TONERDENSITY,
				 &pdev->toner_density)) < 0)
	code = ncode;

    if (pdev->toner_saving_set >= 0 &&
	(code = (pdev->toner_saving_set ?
		 param_write_bool(plist, LIPS_OPTION_TONERSAVING,
				  &pdev->
				  toner_saving) : param_write_null(plist,
								   LIPS_OPTION_TONERSAVING)))
	< 0)
	code = ncode;

    if (pdev->Duplex_set >= 0 &&
	(ncode = (pdev->Duplex_set ?
		  param_write_bool(plist, "Duplex", &pdev->Duplex) :
		  param_write_null(plist, "Duplex"))) < 0)
	code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_FONTDOWNLOAD,
				  &pdev->FontDL)) < 0)
	code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_FACEUP,
				  &pdev->faceup)) < 0) code = ncode;

    pmedia.data = (const byte *)pdev->mediaType,
	pmedia.size = strlen(pdev->mediaType), pmedia.persistent = false;

    if ((ncode = param_write_string(plist, LIPS_OPTION_MEDIATYPE,
				    &pmedia)) < 0) code = ncode;

    pbindloc.data = (const byte *)pdev->BindLoc;
    pbindloc.size = strlen(pdev->BindLoc), pbindloc.persistent = false;

    if ((ncode = param_write_string(plist, LIPS_OPTION_BINDLOC,
                    &pbindloc)) < 0) code = ncode;

    if ((ncode = param_write_int(plist, LIPS_OPTION_GUTTER, &pdev->Gutter)) < 0)
    code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_COLLATE, &pdev->Collate)) < 0)
    code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_LANDSCAPE, &pdev->Landscape)) < 0)
    code = ncode;

    if ((ncode = param_write_int(plist, LIPS_OPTION_OUTBIN, &pdev->OutputBin)) < 0)
    code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_SORT, &pdev->Sort)) < 0)
    code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_STACK, &pdev->Stack)) < 0)
    code = ncode;

    pstaple.data = (const byte *)pdev->Staple;
    pstaple.size = strlen(pdev->Staple), pstaple.persistent = false;

	if ((ncode = param_write_string(plist, LIPS_OPTION_STAPLE,
                    &pstaple)) < 0) code = ncode;

    if ((ncode = param_write_bool(plist, LIPS_OPTION_MODEH, &pdev->ModeH)) < 0)
    code = ncode;

    if (code < 0)
	return code;

    usern.data = (const byte *)pdev->Username,
	usern.size = strlen(pdev->Username), usern.persistent = false;

    return param_write_string(plist, LIPS_OPTION_USER_NAME, &usern);
}

/* Put parameters. */
private int
lbp_common_put_params(gx_device * dev, gs_param_list * plist)
{
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    int ecode = 0;
    int code;
    gs_param_name param_name;
    gs_param_string pmedia;
    int cass = pdev->cassetFeed;
    gs_param_string usern;
    int nup = pdev->nup;
    int toner_density = pdev->toner_density;
    bool toner_saving = pdev->toner_saving;
    bool toner_saving_set = pdev->toner_saving_set;
    bool fontdl = pdev->FontDL;
    bool faceup = pdev->faceup;
    bool duplex;
    int duplex_set = -1;
    int old_bpp = dev->color_info.depth;
    int bpp = 0;

	int		pbindloc_set = OPT_NOT_SPECIFIED;
    gs_param_string pbindloc;
	int		gutter_set = OPT_NOT_SPECIFIED;
    int     gutter;
    bool    collate;
    bool    land;
    int     outbin;
    bool    sort;
    int     sort_set = OPT_NOT_SPECIFIED;
    bool    stack;
    int     stack_set = OPT_NOT_SPECIFIED;
    gs_param_string pstaple;
    bool    modeh;
    const char *dname = pdev->dname; /* get device name */
    int     device_id = 0;
    static int land_set;

    /* device search */
    if (strcmp(dname, "lbp1310") == 0) {
        device_id = LBP1310;
    } else if (strcmp(dname, "lbp1510") == 0) {
        device_id = LBP1510;
    } else if (strcmp(dname, "lbp1610") == 0) {
        device_id = LBP1610;
    } else if (strcmp(dname, "lbp1710") == 0) {
        device_id = LBP1710;
    } else if (strcmp(dname, "lbp1810") == 0) {
        device_id = LBP1810;
    } else if (strcmp(dname, "lbp1910") == 0) {
        device_id = LBP1910;
    } else {
        ecode = gs_error_rangecheck;
    }

	if (cpca_arg.StateFlg == 0) {
		/* cpca param initialize */
		cpca_arg.TonerDensity = -1;
		cpca_arg.TonerSaving = -1;
		cpca_arg.InTrayNum = -1;
		cpca_arg.Duplex = -1;
		cpca_arg.StateFlg = 1;
	}

    /* 用紙種類選択(-sMediaType)(LIPS) */
    switch (code = param_read_string(plist,
                     (param_name = LIPS_OPTION_MEDIATYPE),
                     &pmedia)) {
    case 0:
    if (pmedia.size > LIPS_MEDIACHAR_MAX)
        ecode = gs_error_limitcheck;
    else {          /* Check the validity of ``MediaType'' characters */
        if (!((strcmp(pmedia.data, "PlainPaper") == 0) ||
            (strcmp(pmedia.data, "PlainPaperL") == 0 && (device_id == LBP1510 ||
                                                         device_id == LBP1610 ||
                                                         device_id == LBP1710 ||
                                                         device_id == LBP1810 )) ||
            (strcmp(pmedia.data, "CardBoard") == 0) ||
            (strcmp(pmedia.data, "CardBoardH") == 0 && (device_id == LBP1610 ||
                                                        device_id == LBP1810 )) ||
            (strcmp(pmedia.data, "OHP") == 0))) {
            ecode = gs_error_rangecheck;
        }
		if (strcmp(pmedia.data, "OHP") == 0) {
			/* OHP指定の場合は給紙を手差しトレイとする */
	        cpca_arg.InTrayNum = 1;
		}
		if (strcmp(pmedia.data, "CardBoard") == 0 && (device_id == LBP1310 ||
                                                      device_id == LBP1510 ||
                                                      device_id == LBP1710 ||
                                                      device_id == LBP1910 )) {
			/* LBP-1310,1510,1710,1910で厚紙指定の場合は給紙を手差しトレイとする */
	        cpca_arg.InTrayNum = 1;
		}
    }
    break;
	default:
		ecode = code;
        param_signal_error(plist, param_name, ecode);
	case 1:
		pmedia.data = 0;
	break;
    }

    /* 給紙カセットの選択(機種依存なし)(-dCasset)(CPCA) */
    switch (code = param_read_int(plist,
                  (param_name = LIPS_OPTION_CASSETFEED),
                  &cass)) {
    case 0:
    if (cass < -1 || cass > 17 || (cass > 5 && cass < 10))
        ecode = gs_error_limitcheck;
    else {
		/* OHP指定による給紙固定の場合はこのオプションの指定を無視する */
        if (cpca_arg.InTrayNum != 1) {
			cpca_arg.InTrayNum = cass; /* for cpca param */
		}
        break;
    }
    goto casse;
    default:
    ecode = code;
      casse:param_signal_error(plist, param_name, ecode);
    case 1:
    break;
    }



	/* パネル表示変更(機種依存なし)(-sUserName)(LIPS) */
    switch (code = param_read_string(plist,
				     (param_name = LIPS_OPTION_USER_NAME),
				     &usern)) {
	case 0:
	if (usern.size > LIPS_USERNAME_MAX)
	    ecode = gs_error_limitcheck;
	else {			/* Check the validity of ``User Name'' characters */
	    int i;
	    for (i = 0; i < usern.size; i++)
		if (usern.data[i] < '\x20' || usern.data[i] > '\x7e'
		    /* && usern.data[i] < '\xa0') || usern.data[i] > '\xfe' */
		    ) {
		    ecode = gs_error_rangecheck;
		    goto userne;
		}
		cpca_arg.PanelMsg[0] = usern.size;
		memcpy(cpca_arg.PanelMsg + 1, usern.data, usern.size);
	}
	break;
	goto userne;
	default:
	ecode = code;
      userne:param_signal_error(plist, param_name, ecode);
	case 1:
	usern.data = 0;
	break;
    }

	/* N面印刷設定(機種依存なし)(-dNup)(LIPS) */
    switch (code = param_read_int(plist,
				  (param_name = LIPS_OPTION_NUP), &nup)) {
	case 0:
	if (nup != 1 && nup != 2 && nup != 4)
	    ecode = gs_error_rangecheck;
	else
	    break;
	goto nupe;
	default:
	ecode = code;
      nupe:param_signal_error(plist, param_name, ecode);
	case 1:
	break;
    }

	/* トナー濃度設定(機種依存なし)(-dTonerDensity)(CPCA) */
    switch (code = param_read_int(plist,
				  (param_name = LIPS_OPTION_TONERDENSITY),
				  &toner_density)) {
	case 0:
	if (toner_density < 1 || toner_density > 8)
	    ecode = gs_error_rangecheck;
	else {
		cpca_arg.TonerDensity = toner_density; /* for cpca param */
	    break;
	}
	goto tden;
	default:
	ecode = code;
      tden:param_signal_error(plist, param_name, ecode);
	case 1:
	break;
    }

	/* トナー節約設定(機種依存なし)(-dTonerSaving)(CPCA) */
    if (pdev->toner_saving_set >= 0)
	switch (code =
		param_read_bool(plist, (param_name = LIPS_OPTION_TONERSAVING),
				&toner_saving)) {
	    case 0:
	    toner_saving_set = 1;
		cpca_arg.TonerSaving = toner_saving; /* for cpca param */
	    break;
	    default:
	    if ((code = param_read_null(plist, param_name)) == 0) {
		toner_saving_set = 0;
		break;
	    }
	    ecode = code;
	    param_signal_error(plist, param_name, ecode);
	    case 1:
	    break;
	}

	/* 両面印刷設定(-dDuplex)(CPCA) */
    if (pdev->Duplex_set >= 0)	/* i.e., Duplex is supported */
	switch (code = param_read_bool(plist, (param_name = "Duplex"),
				       &duplex)) {
	    case 0:
	    duplex_set = 1;
		/* 機種依存処理 */
		if (device_id == LBP1510 || device_id == LBP1710) {
			ecode = gs_error_rangecheck;
		} else {
			cpca_arg.Duplex = duplex;
		}
	    break;
	    default:
	    if ((code = param_read_null(plist, param_name)) == 0) {
		duplex_set = 0;
		break;
	    }
	    ecode = code;
	    param_signal_error(plist, param_name, ecode);
	    case 1:
	    break;
	}

	/* フォントダウンロード(この機能は使用しない) */
    if ((code = param_read_bool(plist,
				(param_name = LIPS_OPTION_FONTDOWNLOAD),
				&fontdl)) < 0)
	param_signal_error(plist, param_name, ecode = code);


	/* カラー・モノクロ切替え設定(-dBitPerPixel)(非サポート機能) */
    switch (code = param_read_int(plist, (param_name = "BitsPerPixel"), &bpp)) {
	case 0:
	if (bpp != 8 && bpp != 24)
	    ecode = gs_error_rangecheck;
	else
	    break;
	goto bppe;
	default:
	ecode = code;
      bppe:param_signal_error(plist, param_name, ecode);
	case 1:
	break;
    }

    if (bpp != 0) {
	dev->color_info.depth = bpp;
	dev->color_info.num_components = ((bpp == 8) ? 1 : 3);
	dev->color_info.max_gray = (bpp > 8 ? 255 : 1000);
	dev->color_info.max_color = (bpp > 8 ? 255 : 1000);
	dev->color_info.dither_grays = (bpp > 8 ? 256 : 5);
	dev->color_info.dither_colors = (bpp > 8 ? 256 : 2);
	dev_proc(pdev, map_rgb_color) =
	    ((bpp == 8) ? gx_default_gray_map_rgb_color :
	     gx_default_rgb_map_rgb_color);
	dev_proc(pdev, map_color_rgb) =
	    ((bpp == 8) ? gx_default_gray_map_color_rgb :
	     gx_default_rgb_map_color_rgb);

	((bpp == 8) ? (cpca_arg.ColorMode = 0):(cpca_arg.ColorMode = 1));
    }

	/* とじ方向指定	(-sBindingLocation) (CPCA)	*/
    switch (code = param_read_string(plist,
				     (param_name = LIPS_OPTION_BINDLOC),
				     &pbindloc)) {
	  case 0:
		if (pbindloc.size > LIPS_BINDLOC_MAX){
			ecode = gs_error_limitcheck;
			goto pbindloce;
		}else{
			char	tmp[LIPS_BINDLOC_MAX + 1];
			memcpy( tmp, pbindloc.data, pbindloc.size );
			tmp[pbindloc.size] = 0;
			if (strcmp(tmp, "LongEdge") == 0){
				cpca_arg.Bind_Dir = BINDLOC_LONG;
			}else if (strcmp(tmp, "LongEdge2") == 0){
				cpca_arg.Bind_Dir = BINDLOC_LONG2;
			}else if (strcmp(tmp, "ShortEdge") == 0){
				cpca_arg.Bind_Dir = BINDLOC_SHORT;
			}else if (strcmp(tmp, "ShortEdge2") == 0){
				cpca_arg.Bind_Dir = BINDLOC_SHORT2;
			}else{
				ecode = gs_error_rangecheck;
				goto pbindloce;
	    	}
		}
		pbindloc_set = OPT_SPECIFIED;
		break;
	  case 1:
		pbindloc.data = 0;
		break;
	  default:
		ecode = code;
		pbindloce:param_signal_error(plist, param_name, ecode);
    }

	/* とじ代指定	(-dGutter) (CPCA)			*/
    switch (code = param_read_int(plist,
				  (param_name = LIPS_OPTION_GUTTER),
				  &gutter)) {
	  case 0:
		if (gutter < 0 || gutter > 30){
			ecode = gs_error_limitcheck;
			goto guttere;
		}else{
			cpca_arg.Bind_Marg = gutter;
		}
		gutter_set = OPT_SPECIFIED;
		break;
	  case 1:
		break;
	  default:
		ecode = code;
		guttere:param_signal_error(plist, param_name, ecode);
    }

	/* 部単位印刷指定 (-dCollate) (CPCA)		*/
	switch (code = param_read_bool(plist,
					(param_name = LIPS_OPTION_COLLATE),
				    &collate)) {
	  case 0:
		if (( device_id == LBP1710 ) ||
			( device_id == LBP1810 ) ||
			( device_id == LBP1910 )){
			if ( collate == TRUE ){
				cpca_arg.Collate = COLLATE_SPECIFIED;
			}else{	/* FALSE	*/
	    		ecode = code;
	    		goto collatee;
			}
		}else{	/* サポートしていない機種である	*/
			ecode = gs_error_rangecheck;
	    	goto collatee;
		}
	    break;
	  case 1:
	    break;
	  default:
		if ((code = param_read_null(plist, param_name)) == 0) {
			cpca_arg.Collate = COLLATE_NOT_SPECIFIED;
			break;
		}
		ecode = code;
	    collatee:param_signal_error(plist, param_name, ecode);
	}

	/* 印刷方向指定 (-dLandscape) (LIPS)		*/
	switch (code = param_read_bool(plist,
					(param_name = LIPS_OPTION_LANDSCAPE),
				    &land)) {
	  case 0:
		land_set = 1;
	    break;
	  case 1:
		land = FALSE;
	    break;
	  default:
		if ((code = param_read_null(plist, param_name)) == 0) {
			land = FALSE;
		break;
	    }
	    ecode = code;
	    param_signal_error(plist, param_name, ecode);
	}

	/* 排紙先指定 (-dOutputBin) (CPCA)			*/
    switch (code = param_read_int(plist,
				  (param_name = LIPS_OPTION_OUTBIN),
				  &outbin)) {
	  case 0:
		if (outbin < 1 || outbin > 17){
			ecode = gs_error_limitcheck;
		}else{
			cpca_arg.OutputBin = outbin;
			break;
		}
		goto outbine;
	  case 1:
		break;
	  default:
		ecode = code;
		outbine:param_signal_error(plist, param_name, ecode);
    }

	/* ソート指定 (-dSort) (CPCA)				*/
	switch (code = param_read_bool(plist,
					(param_name = LIPS_OPTION_SORT),
				    &sort)) {
	  case 0:
		if ( device_id == LBP1910 ){
			if ( sort == FALSE ){
		    	ecode = gs_error_rangecheck;
		    	goto sorte;
			}
		}else{	/* サポートしていない機種である	*/
			ecode = gs_error_rangecheck;
	    	goto sorte;
		}
		sort_set = OPT_SPECIFIED;
	    break;
	  case 1:
	    break;
	  default:
		if ((code = param_read_null(plist, param_name)) == 0) {
			break;
	   	}
	   	ecode = code;
	    sorte:param_signal_error(plist, param_name, ecode);
	}

	/* スタック指定 (-dStack) (CPCA)			*/
	switch (code = param_read_bool(plist,
					(param_name = LIPS_OPTION_STACK),
				    &stack)) {
	  case 0:
		if ( device_id == LBP1910 ){
			if ( stack == FALSE ){
	    		ecode = gs_error_rangecheck;
	    		goto stacke;
			}
		}else{	/* サポートしていない機種である	*/
			ecode = gs_error_rangecheck;
	    	goto stacke;
		}
		stack_set = OPT_SPECIFIED;
	    break;
	  case 1:
	    break;
	  default:
		if ((code = param_read_null(plist, param_name)) == 0) {
			break;
	   	}
	   	ecode = code;
	    stacke:param_signal_error(plist, param_name, ecode);
	}

	/* ソート・スタックは同時に指定できない	*/
	if (cpca_arg.SortMode == SS_NOT_SPECIFIED) {
		if ( sort_set == OPT_NOT_SPECIFIED && stack_set == OPT_NOT_SPECIFIED ){
			cpca_arg.SortMode = SS_NOT_SPECIFIED;
		}else if ( sort_set == OPT_SPECIFIED && stack_set == OPT_NOT_SPECIFIED ){
			cpca_arg.SortMode = SS_SORT;
		}else if ( sort_set == OPT_NOT_SPECIFIED && stack_set == OPT_SPECIFIED ){
			cpca_arg.SortMode = SS_STACK;
		}else if ( sort_set == OPT_SPECIFIED && stack_set == OPT_SPECIFIED ){
			ecode = gs_error_rangecheck;
			param_name = LIPS_OPTION_SORT;
			param_signal_error(plist, param_name, ecode);
		}
	}

	/* ステイプル指定 (-sStaple) (CPCA)			*/
    switch (code = param_read_string(plist,
				     (param_name = LIPS_OPTION_STAPLE),
				     &pstaple)) {
	  case 0:
		if ( device_id == LBP1910 ){
			if (pstaple.size > LIPS_STAPLE_MAX){
				ecode = gs_error_limitcheck;
			}else{
				char	tmp[LIPS_STAPLE_MAX + 1];
				memcpy( tmp, pstaple.data, pstaple.size );
				tmp[pstaple.size] = 0;
				if (strcmp(tmp, "LeftTop") == 0){
					cpca_arg.StapleLocate = STAPLE_LEFTTOP;
				}else if (strcmp(tmp, "LeftBottom") == 0){
					cpca_arg.StapleLocate = STAPLE_LEFTBOTTOM;
				}else if (strcmp(tmp, "Left") == 0){
					cpca_arg.StapleLocate = STAPLE_LEFT;
				}else{
					ecode = gs_error_rangecheck;
					goto pstaplee;
		    	}
			}
		}else{	/* サポートしていない機種である	*/
			ecode = gs_error_rangecheck;
	    	goto pstaplee;
		}
		break;
	  case 1:
		pstaple.data = 0;
		break;
	  default:
		ecode = code;
		pstaplee:param_signal_error(plist, param_name, ecode);
    }

	/* 特殊定着モード指定 (-dSpecialModeH)		*/
	switch (code = param_read_bool(plist,
					(param_name = LIPS_OPTION_MODEH),
				    &modeh)) {
	  case 0:
		if (( device_id == LBP1310 ) ||
			( device_id == LBP1910 )){
			if ( modeh == FALSE ){
		    	ecode = code;
		    	goto modehe;
			}
			cpca_arg.SpecialModeH = MODEH_SPECIFIED;
		}else{	/* サポートしていない機種である	*/
			ecode = gs_error_rangecheck;
	    	goto modehe;
		}
	    break;
	  case 1:
	    break;
	  default:
		if ((code = param_read_null(plist, param_name)) == 0) {
			cpca_arg.SpecialModeH = MODEH_NOT_SPECIFIED;
			break;
	    }
	   	ecode = code;
	    modehe:param_signal_error(plist, param_name, ecode);
	}

	/* フェースアップトレイ選択(-dOutputFaceUp)(CPCA) */
    if ((code = param_read_bool(plist,
				(param_name = LIPS_OPTION_FACEUP),
				&faceup)) < 0) {
		param_signal_error(plist, param_name, ecode = code);
    }
	if (faceup) {
		/* 機種依存処理 */
		if (device_id != LBP1910) {
			ecode = gs_error_rangecheck;
		} else {
		    cpca_arg.OutputBin = 2;
		}
    }

    if (ecode < 0)
	return ecode;
    code = gdev_vector_put_params(dev, plist);
    if (code < 0)
	return code;

    pdev->cassetFeed = cass;
    pdev->nup = nup;
    pdev->toner_density = toner_density;
    pdev->toner_saving = toner_saving;
    pdev->toner_saving_set = toner_saving_set;

	pdev->FontDL = fontdl;
    pdev->faceup = faceup;

	pdev->Gutter = gutter;
	pdev->Collate = collate;
	pdev->OutputBin = outbin;
	pdev->Sort = sort;
	pdev->Stack = stack;
	pdev->ModeH = modeh;

	if (land_set == 1) {
	pdev->Landscape = TRUE;
	} else {
	pdev->Landscape = FALSE;
	}

    if (duplex_set >= 0) {
	pdev->Duplex = duplex;
	pdev->Duplex_set = duplex_set;
    }
    if (pmedia.data != 0 &&
	bytes_compare(pmedia.data, pmedia.size,
		      (const byte *)pdev->mediaType, strlen(pdev->mediaType))
	) {
	memcpy(pdev->mediaType, pmedia.data, pmedia.size);
	pdev->mediaType[pmedia.size] = 0;
    }

    if (pbindloc.data != 0 &&
	bytes_compare(pbindloc.data, pbindloc.size,
		      (const byte *)pdev->BindLoc, strlen(pdev->BindLoc))
	) {
		memcpy(pdev->BindLoc, pbindloc.data, pbindloc.size);
		pdev->BindLoc[pbindloc.size] = 0;
    }

    if (pstaple.data != 0 &&
	bytes_compare(pstaple.data, pstaple.size,
		      (const byte *)pdev->Staple, strlen(pdev->Staple))
	) {
		memcpy(pdev->Staple, pstaple.data, pstaple.size);
		pdev->Staple[pstaple.size] = 0;
    }

    if (usern.data != 0 &&
	bytes_compare(usern.data, usern.size,
		      (const byte *)pdev->Username, strlen(pdev->Username))
	) {
		memcpy(pdev->Username, usern.data, usern.size);
		pdev->Username[usern.size] = 0;
    }

    if (bpp != 0 && bpp != old_bpp && pdev->is_open)
	return gs_closedevice(dev);
    return 0;
}

/* ---------------- Images ---------------- */

private int
lips4v_copy_mono(gx_device * dev, const byte * data,
		 int data_x, int raster, gx_bitmap_id id, int x, int y, int w,
		 int h, gx_color_index zero, gx_color_index one)
{
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    stream *s = gdev_vector_stream(vdev);
    int dpi = dev->x_pixels_per_inch;
    gx_drawing_color color;
    int code = 0;
    floatp r, g, b;

    if (id != gs_no_id && zero == gx_no_color_index &&
	one != gx_no_color_index && data_x == 0) {
	gx_drawing_color dcolor;

	color_set_pure(&dcolor, one);
	lips4v_setfillcolor(vdev, &dcolor);

	if (lips4v_copy_text_char(dev, data, raster, id, x, y, w, h) >= 0)
	    return 0;
    }
    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    /*
       (*dev_proc(vdev->bbox_device, copy_mono))
       ((gx_device *)vdev->bbox_device, data, data_x, raster, id,
       x, y, w, h, zero, one);
     */
    if (zero == gx_no_color_index) {
	if (one == gx_no_color_index)
	    return 0;
	/* one 色に染め、透過にする */
	if (pdev->MaskState != 1) {
	    lputs(s, "}H1" LIPS_IS2);
	    pdev->MaskState = 1;
	}
	if (pdev->color_info.depth == 8) {
	    gx_color_index one_color = vdev->color_info.max_gray - one;

	    lputs(s, "}T");
	    sput_lips_int(s, one_color);
	    lputs(s, LIPS_IS2);
	} else {
	    r = (one >> 16) * 1000.0 / 255.0;
	    g = ((one >> 8) & 0xff) * 1000.0 / 255.0;
	    b = (one & 0xff) * 1000.0 / 255.0;
	    lputs(s, "}T");
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	    lputs(s, LIPS_IS2);
	}
    } else if (one == gx_no_color_index)
	/* 1bit は透明 ビット反転・zero 色に染める */
    {
	gx_color_index zero_color = vdev->color_info.max_gray - zero;

	if (pdev->MaskState != 1) {
	    lputs(s, "}H1" LIPS_IS2);
	    pdev->MaskState = 1;
	}
	if (pdev->color_info.depth == 8) {
	    lputs(s, "}T");
	    sput_lips_int(s, zero_color);
	    lputs(s, LIPS_IS2);
	} else {
	    r = (zero >> 16) * 1000.0 / 255.0;
	    g = ((zero >> 8) & 0xff) * 1000.0 / 255.0;
	    b = (zero & 0xff) * 1000.0 / 255.0;
	    lputs(s, "}T");
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	    lputs(s, LIPS_IS2);
	}
    } else if (one == vdev->white) {
	/* ビット反転 白塗り  zero 色に染める */
	gx_color_index zero_color = vdev->color_info.max_gray - zero;

	if (pdev->MaskState != 0) {
	    lputs(s, "}H0" LIPS_IS2);
	    pdev->MaskState = 0;
	}
	if (pdev->color_info.depth == 8) {
	    lputs(s, "}T");
	    sput_lips_int(s, zero_color);
	    lputs(s, LIPS_IS2);
	} else {
	    r = (zero >> 16) * 1000.0 / 255.0;
	    g = ((zero >> 8) & 0xff) * 1000.0 / 255.0;
	    b = (zero & 0xff) * 1000.0 / 255.0;
	    lputs(s, "}T");
	    sput_lips_int(s, r);
	    sput_lips_int(s, g);
	    sput_lips_int(s, b);
	    lputs(s, LIPS_IS2);
	}
    } else {
	if (zero != gx_no_color_index) {
	    code = (*dev_proc(dev, fill_rectangle)) (dev, x, y, w, h, zero);
	    if (code < 0)
		return code;
	}
	if (pdev->MaskState != 1) {
	    lputs(s, "}H1" LIPS_IS2);
	    pdev->MaskState = 1;
	}
	color_set_pure(&color, one);
	code = gdev_vector_update_fill_color((gx_device_vector *) pdev,
					     &color);
    }
    if (code < 0)
	return 0;
    lputs(s, "}P");
    sput_lips_int(s, x);	/* Position X */
    sput_lips_int(s, y);	/* Position Y */
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, h);	/* Height */
    sput_lips_int(s, w);	/* Width */
    lputs(s, "100110" LIPS_IS2);

    lputs(s, "}Q11");

    {
	int i, j;
	uint width_bytes = (w + 7) >> 3;
	uint num_bytes = round_up(width_bytes, 4) * h;
	byte *buf = gs_alloc_bytes(vdev->memory, num_bytes,
				   "lips4v_copy_mono(buf)");

	if (data_x % 8 == 0) {
	    for (i = 0; i < h; ++i) {
		memcpy(buf + i * width_bytes,
		       data + (data_x >> 3) + i * raster, width_bytes);
	    }
	} else {
	    for (i = 0; i < h; ++i) {
		for (j = 0; j < width_bytes; j++) {
		    *(buf + i * width_bytes + j) =
			*(data + (data_x >> 3) + i * raster +
			  j) << (data_x % 8) | *(data + (data_x >> 3) +
						 i * raster + j + 1) >> (8 -
									 data_x
									 % 8);
		}
	    }
	}


	if (one == gx_no_color_index
	    || (one == vdev->white
		&& zero != gx_no_color_index)) lips4v_write_image_data(vdev,
								       buf,
								       num_bytes,
								       TRUE);
	else
	    lips4v_write_image_data(vdev, buf, num_bytes, FALSE);

	gs_free_object(vdev->memory, buf, "lips4v_copy_mono(buf)");
    }

    return 0;
}

/* Copy a color bitmap. */
private int
lips4v_copy_color(gx_device * dev,
		  const byte * data, int data_x, int raster, gx_bitmap_id id,
		  int x, int y, int w, int h)
{
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    gx_device_vector *const vdev = (gx_device_vector *) dev;

    stream *s = gdev_vector_stream(vdev);
    int depth = dev->color_info.depth;
    int dpi = dev->x_pixels_per_inch;
    int num_components = (depth < 24 ? 1 : 3);
    uint width_bytes = w * num_components;

    if (dev->color_info.depth == 8) {
	gx_drawing_color dcolor;

	/* LIPS IV ではグレースケールも単色イメージ・カラー指定命令に
	   影響されるので黒色を指定しなければならない。 */
	color_set_pure(&dcolor, vdev->black);
	lips4v_setfillcolor(vdev, &dcolor);
    } else {
	if (pdev->TextMode) {
	    lputs(s, LIPS_CSI "&}");
	    pdev->TextMode = FALSE;
	}
    }

    if (pdev->MaskState != 0) {
	lputs(s, "}H0" LIPS_IS2);	/* 論理描画設定命令 */
	pdev->MaskState = 0;
    }
    lputs(s, "}P");
    sput_lips_int(s, x);
    sput_lips_int(s, y);
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, h);
    sput_lips_int(s, w);
    sput_lips_int(s, depth / num_components);

	spool_sputc(s, depth < 24 ? '0' : ':', 1, 1);

    lputs(s, "0110" LIPS_IS2);

    {
	int i;
	uint num_bytes = width_bytes * h;
	byte *buf = gs_alloc_bytes(vdev->memory, num_bytes,
				   "lips4v_copy_color(buf)");

	lputs(s, "}Q11");

	for (i = 0; i < h; ++i) {
	    memcpy(buf + i * width_bytes,
		   data + ((data_x * depth) >> 3) + i * raster, width_bytes);
	}

	if (dev->color_info.depth == 8)
	    lips4v_write_image_data(vdev, buf, num_bytes, TRUE);
	else
	    lips4v_write_image_data(vdev, buf, num_bytes, FALSE);

	gs_free_object(vdev->memory, buf, "lips4v_copy_color(buf)");
    }

    return 0;
}

/* Fill a mask. */
private int
lips4v_fill_mask(gx_device * dev,
		 const byte * data, int data_x, int raster, gx_bitmap_id id,
		 int x, int y, int w, int h,
		 const gx_drawing_color * pdcolor, int depth,
		 gs_logical_operation_t lop, const gx_clip_path * pcpath)
{
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    stream *s = gdev_vector_stream(vdev);
    int dpi = dev->x_pixels_per_inch;

    if (w <= 0 || h <= 0)
	return 0;
    if (depth > 1 ||
	gdev_vector_update_fill_color(vdev, pdcolor) < 0 ||
	gdev_vector_update_clip_path(vdev, pcpath) < 0 ||
	gdev_vector_update_log_op(vdev, lop) < 0)
	return gx_default_fill_mask(dev, data, data_x, raster, id,
				    x, y, w, h, pdcolor, depth, lop, pcpath);
#if 1
    (*dev_proc(vdev->bbox_device, fill_mask))
	((gx_device *) vdev->bbox_device, data, data_x, raster, id,
	 x, y, w, h, pdcolor, depth, lop, pcpath);
#endif
    if (id != gs_no_id && data_x == 0) {
	if (lips4v_copy_text_char(dev, data, raster, id, x, y, w, h) >= 0)
	    return 0;
    }
    if (pdev->TextMode) {
	lputs(s, LIPS_CSI "&}");
	pdev->TextMode = FALSE;
    }
    /* 書きだし */
    if (pdev->MaskState != 1) {
	lputs(s, "}H1" LIPS_IS2);	/* 論理描画設定命令 */
	pdev->MaskState = 1;
    }
    lputs(s, "}P");
    sput_lips_int(s, x);
    sput_lips_int(s, y);
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, dpi * 100);
    sput_lips_int(s, h);
    sput_lips_int(s, w);
    lputs(s, "100110" LIPS_IS2);

    lputs(s, "}Q11");

    {
	int i;
	uint width_bytes = (w + 7) >> 3;
	uint num_bytes = round_up(width_bytes, 4) * h;
	byte *buf = gs_alloc_bytes(vdev->memory, num_bytes,
				   "lips4v_fill_mask(buf)");

	for (i = 0; i < h; ++i) {
	    memcpy(buf + i * width_bytes, data + (data_x >> 3) + i * raster,
		   width_bytes);
	}

	lips4v_write_image_data(vdev, buf, num_bytes, FALSE);

	gs_free_object(vdev->memory, buf, "lips4v_fill_mask(buf)");
    }

    return 0;
}

/* ---------------- High-level images ---------------- */

private image_enum_proc_plane_data(lips4v_image_plane_data);
private image_enum_proc_end_image(lips4v_image_end_image);
private const gx_image_enum_procs_t lips4v_image_enum_procs = {
    lips4v_image_plane_data, lips4v_image_end_image
};

/* Start processing an image. */
private int
lips4v_begin_image(gx_device * dev,
		   const gs_imager_state * pis, const gs_image_t * pim,
		   gs_image_format_t format, const gs_int_rect * prect,
		   const gx_drawing_color * pdcolor,
		   const gx_clip_path * pcpath, gs_memory_t * mem,
		   gx_image_enum_common_t ** pinfo)
{
    gx_device_vector *const vdev = (gx_device_vector *) dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *) dev;
    gdev_vector_image_enum_t *pie =
	gs_alloc_struct(mem, gdev_vector_image_enum_t,
			&st_vector_image_enum, "lips4v_begin_image");
    const gs_color_space *pcs = pim->ColorSpace;
    gs_color_space_index index = 0;
    int num_components = 1;
    bool can_do = prect == 0 &&
	(pim->format == gs_image_format_chunky ||

	 pim->format == gs_image_format_component_planar);

    int code;

    if (pie == 0)
	return_error(gs_error_VMerror);
    pie->memory = mem;
    code = gdev_vector_begin_image(vdev, pis, pim, format, prect,
				   pdcolor, pcpath, mem,
				   &lips4v_image_enum_procs, pie);
    if (code < 0)
	return code;
    *pinfo = (gx_image_enum_common_t *) pie;

    if (!pim->ImageMask) {
	index = gs_color_space_get_index(pcs);
	num_components = gs_color_space_num_components(pcs);

	if (pim->CombineWithColor)
	    can_do = false;
	else {
	    switch (index) {
		case gs_color_space_index_DeviceGray:
		if ((pim->Decode[0] != 0 || pim->Decode[1] != 1)
		    && (pim->Decode[0] != 1 || pim->Decode[1] != 0))
		    can_do = false;
		break;
		case gs_color_space_index_DeviceRGB:
		/* LIPS では RGB を反転することはできない */
		if (pim->Decode[0] != 0 || pim->Decode[1] != 1 ||
		    pim->Decode[2] != 0 || pim->Decode[3] != 1 ||
		    pim->Decode[4] != 0)
		    can_do = false;
		break;
		default:
		/* 
		   LIPS では L*a*b* 形式のカラースペースが使えます。
		   CIEBasedABC を使って表現できないこともないのですが、
		   動作確認できないので低レベルの関数にまかせることに
		   しちゃいます。
		   それよりも CMYK のカラースペースが欲しい...
		 */
		can_do = false;
	    }
	}
    }
    if (!can_do)
	return gx_default_begin_image(dev, pis, pim, format, prect,
				      pdcolor, pcpath, mem,
				      &pie->default_info);
    else if (index == gs_color_space_index_DeviceGray) {
	gx_drawing_color dcolor;

	/* LIPS IV ではグレースケールも単色イメージ・カラー指定命令に
	   影響されるので黒色を明示的に指定しなければならない。 */
	color_set_pure(&dcolor, vdev->black);
	lips4v_setfillcolor(vdev, &dcolor);
    }
    if (pim->ImageMask || (pim->BitsPerComponent == 1 && num_components == 1)) {
	if (pim->Decode[0] > pim->Decode[1])
	    pdev->MaskReverse = 1;
	else
	    pdev->MaskReverse = 0;
    }
    /* Write the image/colorimage/imagemask preamble. */
    {
	stream *s = gdev_vector_stream((gx_device_vector *) pdev);
	int ax, ay, bx, by, cx, cy, dx, dy;
	int tbyte;
	gs_matrix imat;
	int interpolate = 0;

	if (pdev->TextMode) {
	    lputs(s, LIPS_CSI "&}");
	    pdev->TextMode = FALSE;
	}
	gs_matrix_invert(&pim->ImageMatrix, &imat);
	gs_matrix_multiply(&imat, &ctm_only(pis), &imat);
	/*
	   [xx xy yx yy tx ty]
	   LIPS の座標系に変換を行なう。

	   }U{Ax}{Ay}{Bx}{By}{Cx}{Cy}{pie->height}{pie->width}
	   {pim->BitsPerComponent}{0}{0}{1} LIPS_IS2
	 */

	/* }Q110{byte} LIPS_IS2 */
	ax = imat.tx;
	ay = imat.ty;
	bx = imat.xx * pim->Width + imat.yx * pim->Height + imat.tx;
	by = imat.xy * pim->Width + imat.yy * pim->Height + imat.ty;
	cx = imat.yx * pim->Height + imat.tx;
	cy = imat.yy * pim->Height + imat.ty;
	dx = imat.xx * pim->Width + imat.tx;
	dy = imat.xy * pim->Width + imat.ty;

	if (pim->ImageMask) {
	    tbyte =
		(pie->width * pim->BitsPerComponent +
		 7) / 8 * num_components * pie->height;
	    pdev->ncomp = 1;
	    if (tbyte == 1) {
		/* LIPS IV では 1 dot のイメージを変形すると座標が狂うバグが
		   ある。よって 1 dot のイメージは矩形として処理する。 */
		pdev->OneBitMask = true;
		/* Draw Rectangle */
		lputs(s, "2");
		sput_lips_int(s, ax);
		sput_lips_int(s, ay);
		sput_lips_int(s, cx - ax);
		sput_lips_int(s, cy - ay);
		sput_lips_int(s, bx - cx);
		sput_lips_int(s, by - cy);
		sput_lips_int(s, dx - bx);
		sput_lips_int(s, dy - by);
		lputs(s, LIPS_IS2);
		return 0;
	    } else {
		/* 描画論理設定命令 - 透過 */
		if (pdev->MaskState != 1) {
		    lputs(s, "}H1" LIPS_IS2);
		    pdev->MaskState = 1;
		}
	    }
	} else {
	    /* 描画論理設定命令 - 白塗り */
	    if (pdev->MaskState != 0) {
		lputs(s, "}H0" LIPS_IS2);
		pdev->MaskState = 0;
	    }
	    pdev->ncomp = num_components;
	}

	lputs(s, "}U");
	sput_lips_int(s, ax);
	sput_lips_int(s, ay);
	sput_lips_int(s, bx);
	sput_lips_int(s, by);
	sput_lips_int(s, cx);
	sput_lips_int(s, cy);
	sput_lips_int(s, pie->height);
	sput_lips_int(s, pie->width);
	sput_lips_int(s, pim->BitsPerComponent);

	if (pim->Interpolate) {
	    if (pim->BitsPerComponent * pie->num_planes == 1)
		interpolate = 1;
	    else
		interpolate = 3;
	}
	if (pim->ImageMask) {	/* 1bit のとき */
	    lputs(s, "0");
	} else {
	    if (index == gs_color_space_index_DeviceGray)
		lputs(s, "0");
	    else {
		if (format == gs_image_format_chunky)	/* RGBRGBRGB... */
		    lputs(s, "\x3A");
		else		/* RRR...GGG...BBB... */
		    lputs(s, "\x3B");
	    }
	}
	if (interpolate > 0)
	    sput_lips_int(s, interpolate);
	lputs(s, LIPS_IS2);
    }
    return 0;
}

/* Process the next piece of an image. */
private int
lips4v_image_plane_data(gx_image_enum_common_t * info,
			const gx_image_plane_t * planes, int height, int *rows_used)
{

    gx_device_vector *const vdev = (gx_device_vector *)info->dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *)info->dev;

    gdev_vector_image_enum_t *pie = (gdev_vector_image_enum_t *) info;

    stream *s = gdev_vector_stream((gx_device_vector *) pdev);
    int y;

    if (pdev->OneBitMask) {
	/*
	   lputs(s, "\200");
	 */
	pie->y += height;
	return 1;
    }
    if (pie->default_info)
	return gx_image_plane_data(pie->default_info, planes, height);
    gx_image_plane_data(pie->bbox_info, planes, height);
    {
	int plane;
	int width_bytes, tbyte;
	byte *buf;

	width_bytes =
	    (pie->width * pie->bits_per_pixel / pdev->ncomp +
	     7) / 8 * pdev->ncomp;
	tbyte = width_bytes * height;
	buf = gs_alloc_bytes(vdev->memory, tbyte, "lips4v_image_data(buf)");

	for (plane = 0; plane < pie->num_planes; ++plane)
	    for (y = 0; y < height; ++y) {
		memcpy(buf + y * width_bytes,
		       planes[plane].data +
		       ((planes[plane].data_x * pie->bits_per_pixel) >> 3)
		       + y * planes[plane].raster, width_bytes);
	    }

	lputs(s, "}Q10");

	if ((pie->bits_per_pixel > 1 && pdev->ncomp == 1) ||
	    pdev->MaskReverse == 0) {
	    lips4v_write_image_data(vdev, buf, tbyte, TRUE);
	} else
	    lips4v_write_image_data(vdev, buf, tbyte, FALSE);

	gs_free_object(vdev->memory, buf, "lips4v_image_data(buf)");

    }

    return (pie->y += height) >= pie->height;
}

private int
lips4v_image_end_image(gx_image_enum_common_t * info,
                       bool draw_last)
{
    gx_device_vector *const vdev = (gx_device_vector *)info->dev;
    gx_device_lips4v *const pdev = (gx_device_lips4v *)info->dev;

    gdev_vector_image_enum_t *pie = (gdev_vector_image_enum_t *) info;
    stream *s = gdev_vector_stream((gx_device_vector *) pdev);
    int code;

    if (pdev->OneBitMask)
	pdev->OneBitMask = false;
    else
	lputs(s, "}Q1100" LIPS_IS2);	/* End of Image */

    pdev->MaskReverse = -1;

    code = gdev_vector_end_image(vdev, (gdev_vector_image_enum_t *) pie,
				 draw_last, pdev->white);
    return code;
}
