/*  
    BeOS Front-end of PDF file reader xpdf.
    Copyright (C) 1997 Benoit Triquet
    Copyright (C) 1998-99 Hubert Figuiere
	Copyright (C) 2000 Michael Pfeiffer
	
    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., 675 Mass Ave, Cambridge, MA 02139, USA.

	$Id: BeOutputDev.cpp,v 1.14 1999/06/09 21:34:21 hub Exp $

*/
//
// Be OutputDev : device for BeOS output for Xpdf
//


#include <interface/Shape.h>
#include <interface/View.h>
#include <interface/Region.h>
#include <interface/Polygon.h>
#include <interface/Bitmap.h>
#include <Picture.h>
#include <UTF8.h>
#include <ctype.h>

#include <stdlib.h>

#include <gtypes.h>
#include <GfxState.h>
#include <GfxFont.h>
#include <XOutputFontInfo.h>
#include <TextOutputDev.h>
#include "BeFontEncoding.h"
#include "BeOutputDev.h"
#include "PDFView.h"

// #define DEBUG 1
// #define MORE_DEBUG 1

#define xoutRound(x) ((int)(x + 0.5))
// #define FONT_SIZE_FACTOR 0.99 // scale font size by this factor
static const char 
	*DEFAULT_FONT_FAMILY = "Dutch801 Rm BT",
	*DEFAULT_FONT_STYLE = "Roman",
	*DEFAULT_JAPANESE_FONT_FAMILY = "Haru",
	*DEFAULT_JAPANESE_FONT_STYLE = "Regular",
	*DEFAULT_JAPANESE_BOLD_FONT_FAMILY = "Haru",
	*DEFAULT_JAPANESE_BOLD_FONT_STYLE = "Bold";

static const rgb_color WHITE_COLOR = {255,255,255, 0};
static const rgb_color LINK_COLOR = { 0, 0, 255, 0};

#ifndef JAPANESE_FONT_SUPPORT
GBool useEUCJP = false;
#else
GBool userEUCJP = true;
#endif

/////////////////////////////////////////////////////////////////////////
BeOutputDev::BeOutputDev(BView * view, PDFView *parent)
	: mView (view)
{
	mState = NULL;
	mClip = NULL;
	mParent = parent;
	mText = new TextPage(useEUCJP ? textOutASCII7 : textOutLatin1, gFalse);

	// set up the font cache and fonts
	gfxFont = NULL;
	font = NULL;
	fontCache = new BeOutputFontCache();
	mStroke.alpha = mFill.alpha = 255;
}


/////////////////////////////////////////////////////////////////////////
BeOutputDev::~BeOutputDev()
{
	_State *s;
	while (mState != NULL) {
		s = mState;
		mState = s->next;
		delete s;
	}
	delete mClip;
	delete fontCache;
}


#pragma mark -


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::startDoc() {
	fontCache->startDoc(8);
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::startPage(int /*pageNum*/, GfxState * /*state*/)
{
	_State * s;
	// erase view
	if (!mView->IsPrinting()) {
		mView->SetHighColor(WHITE_COLOR);
		mView->FillRect(mView->Bounds());
	}
	
	while (mState != NULL) {
		s = mState;
		mState = s->next;
		delete s;
	}
	mState = NULL;
	delete mClip;
	mClip = new BRegion ();
	if (mView->IsPrinting()) {
		mClip->Set(BRect(0, 0, 1000, 1000));
	} else {
		mClip->Set (mView->Bounds());
	}
	mView->ConstrainClippingRegion (mClip);
	// clear text object
	mText->clear();
}
/////////////////////////////////////////////////////////////////////////
void BeOutputDev::endPage()
{
	mText->coalesce();
}
/////////////////////////////////////////////////////////////////////////
void BeOutputDev::beginString(GfxState *state, GString *s)
{
	mText->beginString(state, s, gFalse/*font ? font->isHex() : gFalse*/);
}
/////////////////////////////////////////////////////////////////////////
void BeOutputDev::endString(GfxState *state)
{
	mText->endString();
}
/////////////////////////////////////////////////////////////////////////
void BeOutputDev::dump()
{
	mView->Flush();
	if ((mParent != NULL) && !mView->IsPrinting()) mParent->Dump();
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::drawLinkBorder( double x1, double y1, double x2,
 double y2, double w)
{
	int x, y;
	BPoint pt1, pt2, pt3, pt4;

#ifdef DEBUG
	fprintf (stderr, "xpdf: BeOutputDev::drawLinkBorder() called\n");
	fprintf (stderr, "xpdf: w = %lf\n", w);
#endif
	/*
	rgb_color color = mView->HighColor();
	rgb_color linkColor = {0, 255, 0, 0};
	mView->SetHighColor(linkColor);
	*/
	if (w < 1) w = 1;
	mView->SetPenSize (w);
	mView->BeginLineArray (4);
	cvtUserToDev (x1, y1, &x, &y);
	pt1.x = x;
	pt1.y = y;
	cvtUserToDev (x2, y1, &x, &y);
	pt2.x = x;
	pt2.y = y;
	cvtUserToDev (x2, y2, &x, &y);
	pt3.x = x;
	pt3.y = y;
	cvtUserToDev (x1, y2, &x, &y);
	pt4.x = x;
	pt4.y = y;
	mView->AddLine (pt1, pt2, LINK_COLOR);
	mView->AddLine (pt2, pt3, LINK_COLOR);
	mView->AddLine (pt3, pt4, LINK_COLOR);
	mView->AddLine (pt4, pt1, LINK_COLOR);
	mView->EndLineArray ();
	// mView->SetHighColor(color);
}


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::saveState( GfxState * /*state*/ )
{
	_State *st = new _State;
	st->next = mState;
	st->low = mStroke; // mView->LowColor( );
	st->high = mFill; // mView->HighColor( );
	st->clip.MakeEmpty ();
	st->clip.Include (mClip);
	mState = st;
}


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::restoreState( GfxState * /*state*/ )
{
	if( mState == NULL ) {
		return;
	}
	
	_State *st = mState;
	// mView->SetLowColor (st->low);
	mStroke = st->low;
	mView->SetHighColor (st->high);
	mFill = st->high;
	mClip->MakeEmpty ();
	mClip->Include (&st->clip);
	mView->ConstrainClippingRegion (mClip);
	mState = st->next;
	delete st;
}


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateAll( GfxState *state )
{
	double *ctm = state->getCTM( );
	updateCTM( state, ctm[ 0 ], ctm[ 1 ], ctm[ 2 ], ctm[ 3 ], ctm[ 4 ],
				ctm[ 5 ] );
	updateLineDash( state );
	updateFlatness( state );
	updateLineJoin( state );
	updateLineCap( state );
	updateMiterLimit( state );
	updateLineWidth( state );
	updateFillColor( state );
	updateStrokeColor( state );
	updateFont( state );
}



/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateCTM(GfxState *state, double m11, double m12,
			 double m21, double m22, double m31, double m32) {
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateLineDash(GfxState *state) {
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateLineJoin(GfxState *state) {
	// FIXME: locks when printing 
	if (mView->IsPrinting()) return;

	join_mode join;
 	switch (state->getLineJoin()) {
	case 0: join = B_MITER_JOIN; 
		break;
	case 1: join = B_ROUND_JOIN; 
		break;
	case 2: join = B_BEVEL_JOIN; 
		break;
	default:
		// error(-1, "Bad line join style (%d)", state->getLineJoin());
		join = B_MITER_JOIN;
	}
	mView->SetLineMode(mView->LineCapMode(), join, mView->LineMiterLimit());
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateLineCap(GfxState *state) {
	// FIXME: locks when printing 
	if (mView->IsPrinting()) return;

	cap_mode cap;
	switch (state->getLineCap()) {
	case 0: cap = B_BUTT_CAP; 
		break;
	case 1: cap = B_ROUND_CAP; 
		break;
	case 2: cap = B_SQUARE_CAP; 
		break;
	default:
		// error(-1, "Bad line cap style (%d)", state->getLineCap());
		cap = B_BUTT_CAP;
	}
	mView->SetLineMode(cap, mView->LineJoinMode(), mView->LineMiterLimit());
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateMiterLimit(GfxState *state) {
	// FIXME: locks when printing 
	if (mView->IsPrinting()) return;

	mView->SetLineMode(mView->LineCapMode(), mView->LineJoinMode(), state->getMiterLimit());
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateFlatness( GfxState *state )
{
#ifdef DEBUG
	fprintf (stderr, "xpdf: updateFlatness()\n");
#endif

	mFlatness = state->getFlatness ();
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateLineWidth(GfxState *state)
{
float w = state->getTransformedLineWidth();
	if (w < 1) w = 1;
	mView->SetPenSize (w);
}


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateFillColor( GfxState *state )
{
	GfxColor *co = state->getFillColor ();
	mFill.red = 0xFF * co->getR ();
	mFill.green = 0xFF * co->getG ();
	mFill.blue = 0xFF * co->getB ();
}


/////////////////////////////////////////////////////////////////////////
void BeOutputDev::updateStrokeColor( GfxState *state )
{
	GfxColor *co = state->getStrokeColor ();
	mStroke.red = 0xFF * co->getR ();
	mStroke.green = 0xFF * co->getG ();
	mStroke.blue = 0xFF * co->getB ();
}

void BeOutputDev::updateFont (GfxState *state)
{
#ifdef MORE_DEBUG
	fprintf (stderr, "xpdf: BeOutputDev::updateFont() called\n");
#endif
  double m11, m12, m21, m22;

  if (!(gfxFont = state->getFont())) {
    font = NULL;
    return;
  }
  state->getFontTransMat(&m11, &m12, &m21, &m22);
  m11 *= state->getHorizScaling();
  m21 *= state->getHorizScaling();
  font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
/*
  if (font) {
    font->update(mStroke, mFill);
  }
*/
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::drawChar(GfxState *state, double x, double y,
			double dx, double dy, Guchar c)
{
	mText->addChar(state, x, y, dx, dy, c);
	if (!font)
		return;

	// check for invisible text -- this is used by Acrobat Capture
	if ((state->getRender() & 3) == 3)
		return;

	double x1, y1;
	state->transform(x, y, &x1, &y1);
	// set stroke color for character
	if (state->getRender() & 1) {
		mView->SetHighColor(mStroke);
	}
	else {
		mView->SetHighColor(mFill);
	}

	font->drawChar(state, NULL, mView, xoutRound(x1), xoutRound(y1), c);

	mView->SetHighColor(mStroke);
}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::drawChar16(GfxState *state, double x, double y,
			double dx, double dy, int c)
{
	if (!font)
		return;

	if (state->getFont()) {
		mText->addChar16(state, x, y, dx, dy, c, gfxFont->getCharSet16());
	}

	// check for invisible text -- this is used by Acrobat Capture
	if ((state->getRender() & 3) == 3)
		return;

	double x1, y1;
	state->transform(x, y, &x1, &y1);

	// set stroke color for character
	if (state->getRender() & 1) {
		mView->SetHighColor(mStroke);
	}
	else {
		mView->SetHighColor(mFill);
	}

	font->drawChar16(state, NULL, mView, xoutRound(x1), xoutRound(y1), c);

	mView->SetHighColor(mStroke);
}

/////////////////////////////////////////////////////////////////////////
/*
void BeOutputDev::drawString (GfxState *state, GString *s)
{
	double x1, y1;
	state->transform( state->getCurX( ), state->getCurY( ), &x1, &y1);
	mView->DrawString (s->getCString(), BPoint (x1, y1));
}
*/

/////////////////////////////////////////////////////////////////////////
GBool BeOutputDev::upsideDown()
{
#ifdef DEBUG
	fprintf (stderr, "xpdf: BeOutputDev::upsideDown() called\n");
#endif

	return gTrue;
}


/////////////////////////////////////////////////////////////////////////
GBool BeOutputDev::useDrawChar()
{
#ifdef DEBUG
	fprintf (stderr, "xpdf: BeOutputDev::useDrawChar() called\n");
#endif

	return gTrue;
}


/////////////////////////////////////////////////////////////////////////

#define DO_IMAGE(L_INIT, L_COND, L_INC, C_INIT, C_COND, C_INC) 	\
	for (L_INIT; L_COND; L_INC) { 							\
		for (C_INIT; C_COND; C_INC) { 						\
			imgStr->getPixel (pixel);						\
			colorMap->getColor (pixel, &currentColor);		\
			unsigned char *p = &pixLine[bpr * y + 4 * x];	\
			p[0] = 255.f * currentColor.getB();				\
			p[1] = 255.f * currentColor.getG();				\
			p[2] = 255.f * currentColor.getR();				\
		}													\
	}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::drawImage(GfxState *state, Stream *str, int width,
			 int height, GfxImageColorMap *colorMap,
			 GBool inlineImg)
{
#ifdef IMAGE_DEBUG
	fprintf (stderr, "BeOutputDev::drawImage() called\n");
	fprintf (stderr, "width = %d; height = %d\n", width, height);
	fprintf (stderr, "inline ? %d\n", inlineImg);
#endif

	double xt, yt, wt, ht;
	BBitmap * bitmap = NULL;
	int nBits, nVals, nComps;
	Guchar pixel [4] = { 0, 0, 0, 0 };
	GfxColor currentColor;
	bool rotate, xFlip, yFlip;	//iamge rotation parameters.
	int j, k;
	int dk;					// increment step for image
	float x0, y0;			// top left corner of image
	float w0, h0;		// size of image
	int32 bpr;			//bytes per row
	
	state->transform (0, 0, &xt, &yt);
/*
	state->transform (1, 0, &wt, &ht);
	printf("%f %f %f %f ", xt, yt, wt, ht);
	state->transform (0, 1, &wt, &ht);
	printf("%f %f ", wt, ht);
	state->transformDelta (1, 0, &wt, &ht);
	printf("%f %f\n", wt, ht);
	state->transformDelta (0, 1, &wt, &ht);
	printf("%f %f\n", wt, ht);
*/
	state->transformDelta (1, 1, &wt, &ht);
//	printf("%f %f\n", wt, ht);

	if (wt > 0) {
		x0 = xt;
		w0 = wt;
	}
	else {
		x0 = xt + wt;
		w0 = -wt;
	}
	if (ht > 0) {
		y0 = yt;
		h0 = ht;
	} 
	else {
		y0 = yt + ht;
		h0 = -ht;
	}
	/*
		compute rotation parameters.
	*/	
	state->transformDelta (1, 0, &xt, &yt);
	rotate = fabs (xt) < fabs (yt);
	int w, h;
	if (rotate) {
		xFlip = ht < 0;
		yFlip = wt > 0;
		w = height; h = width;
	}
	else {
		xFlip = wt < 0;
		yFlip = ht > 0;
		w = width; h = height;
	}

	
	nComps = colorMap->getNumPixelComps();
	nVals = width * nComps;
	nBits = colorMap->getBits();
	
	bitmap = new BBitmap (BRect (0, 0, w - 1, h - 1), B_RGB32);

	bpr = bitmap->BytesPerRow ();
	unsigned char *pixLine = (unsigned char*) bitmap->Bits();

	// initialize the image stream
	ImageStream *imgStr = new ImageStream(str, width, nComps, nBits);
	imgStr->reset();

	int x, y;
//	printf("rotate %d xFlip %d yFlip %d\n", rotate, xFlip, yFlip);
//	printf("width %d height %d\n", width, height); fflush(stdout);
	if (!rotate) {
		if (!xFlip) {
			if (!yFlip) { 
				DO_IMAGE(y = 0, y < h, y++, x = 0, x < w, x++)
			} else { // flip y
				DO_IMAGE(y = h - 1, y >= 0, y--, x = 0, x < w, x++)
			} 
		} else { // flip x
			if (!yFlip) {
				DO_IMAGE(y = 0, y < h, y++, x = w - 1, x >= 0, x--)
			} else { // flip y
				DO_IMAGE(y = h - 1, y >= 0, y--, x = w - 1, x >= 0, x--)
			}
		}
	} else { // rotate
		if (!xFlip) {
			if (!yFlip) { 
				DO_IMAGE(x = 0, x < w, x++, y = 0, y < h, y++)
			} else { // flip y
				DO_IMAGE(x = w - 1, x >= 0, x--, y = 0, y < h, y++)
			} 
		} else { // flip x
			if (!yFlip) {
				DO_IMAGE(x = 0, x < w, x++, y = h - 1, y >= 0, y--)
			} else { // flip y
				DO_IMAGE(x = w - 1, x >= 0, x--, y = h - 1, y >= 0, y--)
			}
		}
	}

	mView->DrawBitmap (bitmap, BRect (x0, y0, x0 + w0, y0 + h0));
	
	delete imgStr;
	delete bitmap;
}



/////////////////////////////////////////////////////////////////////////	
#define DO_MASK(L_INIT, L_COND, L_INC, C_INIT, C_COND, C_INC) 	\
	for (L_INIT; L_COND; L_INC) { 							\
		for (C_INIT; C_COND; C_INC) { 						\
			imgStr->getPixel (&pixel);						\
			unsigned char *p = &pixLine[bpr * y + 4 * x];	\
			if (invert) pixel ^= 1;							\
			if (pixel == 0) {								\
				p [0] = color.blue;							\
				p [1] = color.green;						\
				p [2] = color.red;							\
			} else {										\
				p [3] = 0;								\
			}												\
		}													\
	}

/////////////////////////////////////////////////////////////////////////
void BeOutputDev::drawImageMask(GfxState *state, Stream *str,
				 int width, int height, GBool invert,
				 GBool inlineImg)
{
#ifdef DEBUG
	fprintf (stderr, "xpdf: BeOutputDev::drawImageMask() called\n");
#endif

	double xt, yt, wt, ht;
	BBitmap * bitmap = NULL;
	// int nBits, nVals, nComps;
	Guchar pixel;
	GfxColor currentColor;
	bool rotate, xFlip, yFlip;	//iamge rotation parameters.
	int j, k;
	int dk;					// increment step for image
	float x0, y0;			// top left corner of image
	float w0, h0;		// size of image
	int32 bpr;			//bytes per row
	
	GfxColor *gfxColor = state->getFillColor();
	struct rgb_color color;
	color.red = gfxColor->getR() * 255;
	color.green = gfxColor->getG() * 255;
	color.blue = gfxColor->getB() * 255;
	color.alpha = 255;
	
	state->transform (0, 0, &xt, &yt);
	state->transformDelta (1, 1, &wt, &ht);
	if (wt > 0) {
		x0 = xt;
		w0 = wt;
	}
	else {
		x0 = xt + wt;
		w0 = -wt;
	}
	if (ht > 0) {
		y0 = yt;
		h0 = ht;
	} 
	else {
		y0 = yt + ht;
		h0 = -ht;
	}
	/*
		compute rotation parameters.
	*/	
	state->transformDelta (1, 0, &xt, &yt);
	rotate = fabs (xt) < fabs (yt);
	int w, h;
	if (rotate) {
		xFlip = ht < 0;
		yFlip = wt > 0;
		w = height; h = width;
	}
	else {
		xFlip = wt < 0;
		yFlip = ht > 0;
		w = width; h = height;
	}

	
	// nComps = colorMap->getNumPixelComps();
	// nVals = width * nComps;
	// nBits = colorMap->getBits();
	
	bitmap = new BBitmap (BRect (0, 0, w -1, h - 1), B_RGBA32);

	bpr = bitmap->BytesPerRow ();
	unsigned char *pixLine = (unsigned char*) bitmap->Bits();

	// initialize the image stream
	ImageStream *imgStr = new ImageStream(str, width, 1, 1);
	imgStr->reset();

	int x, y;
//	printf("rotate %d xFlip %d yFlip %d\n", rotate, xFlip, yFlip);
//	printf("width %d height %d\n", width, height); fflush(stdout);
	if (!rotate) {
		if (!xFlip) {
			if (!yFlip) { 
				DO_MASK(y = 0, y < h, y++, x = 0, x < w, x++)
			} else { // flip y
				DO_MASK(y = h - 1, y >= 0, y--, x = 0, x < w, x++)
			} 
		} else { // flip x
			if (!yFlip) {
				DO_MASK(y = 0, y < h, y++, x = w - 1, x >= 0, x--)
			} else { // flip y
				DO_MASK(y = h - 1, y >= 0, y--, x = w - 1, x >= 0, x--)
			}
		}
	} else { // rotate
		if (!xFlip) {
			if (!yFlip) { 
				DO_MASK(x = 0, x < w, x++, y = 0, y < h, y++)
			} else { // flip y
				DO_MASK(x = w - 1, x >= 0, x--, y = 0, y < h, y++)
			} 
		} else { // flip x
			if (!yFlip) {
				DO_MASK(x = 0, x < w, x++, y = h - 1, y >= 0, y--)
			} else { // flip y
				DO_MASK(x = w - 1, x >= 0, x--, y = h - 1, y >= 0, y--)
			}
		}
	}
	drawing_mode dm = mView->DrawingMode();
	mView->SetDrawingMode(B_OP_ALPHA);
	mView->DrawBitmap (bitmap, BRect (x0, y0, x0 + w0, y0 + h0));
	mView->SetDrawingMode(dm);
	delete imgStr;
	delete bitmap;
}



/////////////////////////////////////////////////////////////////////////
/*
	Stroke the current path stored in state
*/
void BeOutputDev::stroke(GfxState *state)
{
#ifdef MORE_DEBUG
	fprintf (stderr, "xpdf: stroke()\n");
#endif
	int i;
	GfxPath *path = state->getPath();
	GfxSubpath *subPath;
	int numSubPath;
	rgb_color oldColor;

	oldColor = mView->HighColor ();
	mView->SetHighColor (mStroke);

	float penSize = mView->PenSize();
	double width = state->getTransformedLineWidth();
	if (width < 1) width = 1;
	mView->SetPenSize(width);	
	numSubPath = path->getNumSubpaths();

	BShape shape;
	for (i = 0; i < numSubPath; i++) {	
		subPath = path->getSubpath(i);
		SubPathToShape (state, subPath, &shape);
		mView->MovePenTo (B_ORIGIN);
		mView->StrokeShape (&shape);
		shape.Clear();
	}

	mView->SetPenSize(penSize);
	mView->SetHighColor (oldColor);
}



/////////////////////////////////////////////////////////////////////////
/*
	Fill the current path stored in state
	BShape seems really buggy in R4 PowerPC (untested on Intel, but tech 
	support to not have it: they run Intel of course :-( )
*/
void 
BeOutputDev::fill(GfxState *state)
{
#ifdef MORE_DEBUG
	fprintf (stderr, "xpdf: fill()\n");
#endif	
	int i;
	GfxPath *path = state->getPath();
	GfxSubpath *subPath;
	int numSubPath;
	rgb_color oldColor;
	
	oldColor = mView->HighColor ();
	mView->SetHighColor (mFill);
	numSubPath = path->getNumSubpaths();
	BShape shape;
	for (i = 0; i < numSubPath; i++) {	
		subPath = path->getSubpath(i);

		SubPathToShape (state, subPath, &shape);
		mView->MovePenTo (B_ORIGIN);
		mView->FillShape (&shape);
		shape.Clear();

	}
	mView->SetHighColor (oldColor);
}


void 
BeOutputDev::eoFill(GfxState *state)
{
	fill (state);
}

/////////////////////////////////////////////////////////////////////////
void
BeOutputDev::clip(GfxState *state)
{
/*	BRegion * region;*/
	BRegion * currentClip, * newClip;
	int i;
	GfxPath *path = state->getPath();
	GfxSubpath *subPath;
	int numSubPath;
	BPicture * pict;
	BView * offView;

	pict = new BPicture ();
	offView = new BView (mView->Frame(), "", B_FOLLOW_NONE, 0);
	mView->AddChild (offView);
	offView->BeginPicture (pict);
	numSubPath = path->getNumSubpaths();
	BShape shape;
	for (i = 0; i < numSubPath; i++) {	
		subPath = path->getSubpath(i);

		SubPathToShape (state, subPath, &shape);
		offView->MovePenTo (B_ORIGIN);
		offView->FillShape (&shape);
		shape.Clear();
	}

	offView->EndPicture ();	
	mView->RemoveChild (offView);
	currentClip = new BRegion ();
	mView->ClipToPicture (pict, B_ORIGIN, true);
	mView->GetClippingRegion (currentClip);
	mClip->IntersectWith (currentClip);
	mView->ConstrainClippingRegion (mClip);
	delete currentClip;
	
	delete pict;
}


void
BeOutputDev::eoClip(GfxState *state)
{
	clip (state);
}

/////////////////////////////////////////////////////////////////////////
GBool BeOutputDev::findText(char *s, GBool top, GBool bottom,
							int *xMin, int *yMin, int *xMax, int *yMax) {
	double xMin1, yMin1, xMax1, yMax1;
  
	xMin1 = (double)*xMin;
	yMin1 = (double)*yMin;
	xMax1 = (double)*xMax;
	yMax1 = (double)*yMax;
	if (mText->findText(s, top, bottom, &xMin1, &yMin1, &xMax1, &yMax1)) {
		*xMin = xoutRound(xMin1);
		*xMax = xoutRound(xMax1);
		*yMin = xoutRound(yMin1);
		*yMax = xoutRound(yMax1);
		return gTrue;
	}
	return gFalse;
}

/////////////////////////////////////////////////////////////////////////
GString *BeOutputDev::getText(int xMin, int yMin, int xMax, int yMax) {
  return mText->getText((double)xMin, (double)yMin,
			   (double)xMax, (double)yMax);
}


#pragma mark -

/////////////////////////////////////////////////////////////////////////
/*
	Convert a GfxSubpath to a BPolygon... for Be rendering
	
	This function is currently obsolete since we use Bezier.
*/
void
BeOutputDev::AddSubPathToPoly (GfxState *state, /*const */GfxSubpath * subPath, 
							BPolygon * poly)
/* subPath should be const, but it is not */
{
	int numPoint;
	BPoint currentPt;
	double x, y;
	int j;
	
	if (poly == NULL) {
		fprintf (stderr, "WARNING: AddSubPathToPoly(): poly NULL!\n");
	}
	numPoint = subPath->getNumPoints();
	for (j = 0; j < numPoint; j++) {
		/*
			Skip curve point. Will change when we'll do bezier curves.
		*/
		if (subPath->getCurve(j) == gFalse) {
			state->transform (subPath->getX(j), subPath->getY(j), &x, &y);
			currentPt.x = x;
			currentPt.y = y;
			poly->AddPoints (&currentPt, 1);
		}
	}
}

/////////////////////////////////////////////////////////////////////////
/*
	Stroke a GfxSubpath. Since R3, BeOS supports Bezier drawing, so I don't
	need to implement it as a polygon... and thus I make smooth curves.

	We use StrokeBezier rather than BShape() because BShape seems to close the
	path and thus does NOT draw things correctly.
*/
void
BeOutputDev::SubPathToStrokeBezier (GfxState *state, /*const */GfxSubpath * subPath, 
							BView * view)
/* subPath should be const, but it is not */
{
	int numPoint;
	BPoint bezier[4];
	double x0, y0, x1, y1, x2, y2, x3, y3;
	int j;

	state->transform (0, 0, &x0, &y0);

#ifdef DEBUG
	if (subPath == NULL) {
		fprintf (stderr, "BeOutputDev::SubPathToStrokeBezier() subPath == NULL\n");
	}
#endif
	numPoint = subPath->getNumPoints();

	j = 0;
	while (j < numPoint) {
		if ((j >= 0) && (subPath->getCurve (j))) {
			state->transform (subPath->getX(j), subPath->getY(j), &x1, &y1);
			state->transform (subPath->getX(j + 1), subPath->getY(j + 1), &x2, &y2);
			state->transform (subPath->getX(j + 2), subPath->getY(j + 2), &x3, &y3);
			
			bezier [0] = BPoint (x0, y0);
			bezier [1] = BPoint (x1, y1);
			bezier [2] = BPoint (x2, y2);
			bezier [3] = BPoint (x3, y3);

			view->StrokeBezier (bezier);
			
			j +=3;
			x0 = x3;
			y0 = y3;
		}
		else {
			state->transform (subPath->getX(j), subPath->getY(j), &x1, &y1);
			
			if (j > 0) {
				view->StrokeLine (BPoint (x0, y0), BPoint (x1, y1));
			}
			
			x0 = x1;
			y0 = y1;
			j++;
		}
	}
}


/////////////////////////////////////////////////////////////////////////
/*
	Convert a GfxSubpath to a BShape... for Be rendering
*/
void
BeOutputDev::SubPathToShape (GfxState *state, /*const */GfxSubpath * subPath, 
							BShape * shape)
/* subPath should be const, but it is not */
{
	int numPoint;
	BPoint bezier[3];
	double x, y;
	int j;

	shape->Clear ();
	numPoint = subPath->getNumPoints();
	
	state->transform (subPath->getX(0), subPath->getY(0), &x, &y);	
	bezier [0].x = x;
	bezier [0].y = y;

	shape->MoveTo (bezier [0]);
	for (j = 1; j < numPoint; j++) {
		/*
			Skip curve point. Will change when we'll do bezier curves.
		*/
		state->transform (subPath->getX(j), subPath->getY(j), &x, &y);
		bezier [0].x = x;
		bezier [0].y = y;
		if (subPath->getCurve (j) == gFalse) {
			shape->LineTo (bezier [0]);	
		}
		else {
			j++;
			if (j >= numPoint) {
				/* error */
#ifdef DEBUG
				fprintf (stderr, "could not complete bezier\n");
#endif
				return;
			}
			if (subPath->getCurve (j) == gFalse) {
#ifdef DEBUG
				fprintf (stderr, "not a curve point\n");
#endif
			}
			state->transform (subPath->getX(j), subPath->getY(j), &x, &y);
			bezier [1].x = x;
			bezier [1].y = y;
			j++;
			if (j >= numPoint) {
				/* error */
#ifdef DEBUG
				fprintf (stderr, "could not complete bezier\n");
#endif
				return;
			}
			if (subPath->getCurve (j) == gTrue) {
#ifdef DEBUG
				fprintf (stderr, "is a curve point\n");
#endif
			}
			state->transform (subPath->getX(j), subPath->getY(j), &x, &y);
			bezier [2].x = x;
			bezier [2].y = y;
			
			shape->BezierTo (bezier);
		}
	}
}


