/*	Willows Software, Inc. - Feb 1997	*/
/*	"@(#)GetGlyphOutlineMac.c	1.1 :/users/sccs/src/win/mac/s.GetGlyphOutlineMac.c 2/6/97 17:40:12" */

// ===========================================================================
//	GetGlyphOutline.c										Craig M. Ambrose
// ===========================================================================
//
//	GetGlyphOutline routine and utilites for converting data structures

#include	<FixMath.h>
#include	<Fonts.h>
#include	<Memory.h>
#include	<QuickDraw.h>
#include	<QDOffscreen.h>
#include	<ToolUtils.h>

#include	"DrvDC.h"
#include	"access library.h"
#include	"windows.h"


//
//	Prototypes
//
Boolean	BuildGlyphMetrics( char theChar, LPDRIVERDC driverData, LPGLYPHMETRICS lpgm, short* yMax );


//
//	Private functions
//


//
//	BuildGlyphMetrics():
//
//	This routine fills in the fields of a GLYPHMETRICS record for the given character
//	in the specified GrafPort. It returns true if the metrics were obtainable and
//	false if the current font isn't a TrueType font.
//

Boolean
BuildGlyphMetrics( char theChar, LPDRIVERDC driverData, LPGLYPHMETRICS lpgm, short* yMax )
{
	GrafPtr		savePort;
	GrafPtr		thePort = (GrafPtr )driverData->grafPort;
	FontInfo	fontInfo;
	Point		numer = {1, 1};
	Point		denom = {1, 1};
	Fixed		advWidth;
	Fixed		leftSideBearing;
	Rect		bounds;
	OSErr		theErr;

//
//	Set the port to our port and set the font info
//
	GetPort( &savePort );
	SetPort( thePort );
	TextFont( driverData->lpFontData->fontID );
	TextFace( driverData->lpFontData->face );
	TextSize( driverData->lpFontData->fontSize );
	
	if ( IsOutline( numer, denom ) == false )
		return( false );

//
//	Get the Macintosh metrics
//
	GetFontInfo( &fontInfo );
	theErr = OutlineMetrics( 1, &theChar, numer, denom, yMax, nil,
								&advWidth, &leftSideBearing, &bounds );
	if ( theErr ) {
//
//	The current font is probably a bitmap font so we need to dummy up the OutlineMetrics results
//
		*yMax = fontInfo.ascent;
		advWidth = Long2Fix( fontInfo.widMax );
		leftSideBearing = 0;
		bounds.top = 0;
		bounds.left = 0;
		bounds.bottom = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
		bounds.right = fontInfo.widMax;
	}

//
//	Convert the Macintosh metrics to those that Windoze can use
//
	lpgm->gmBlackBoxX = bounds.right;
	lpgm->gmBlackBoxY = bounds.bottom - bounds.top;
//
//	The Windoze origin is top-left, the Macintosh uses the baseline-left
//
	lpgm->gmptGlyphOrigin.x = FixRound( leftSideBearing );
	lpgm->gmptGlyphOrigin.y = *yMax;
	lpgm->gmCellIncX = FixRound( advWidth );
	lpgm->gmCellIncY = 0;

//
//	Restore the port
//
	SetPort( savePort );
	return( true );
}


//
//	Public/Exported functions
//

//
//	TWIN_GetGlyphOutline():
//
//	The Macintosh version of the GetGlyphOutline call.  It is passed the Mac-specific
//	driver data that contains the GrafPtr and similar information.  It then does all
//	of the work using Mac Toolbox calls
//

DWORD
TWIN_GetGlyphOutline(	LPDRIVERDC		driverData,
						UINT			uChar,
						UINT			fuFormat,
						LPGLYPHMETRICS	lpgm,
						DWORD			cbBuffer,
						LPVOID			lpvBuffer,
						CONST MAT2*		lpmat2 )
{
	TransMatrix		m1;
	GrafPtr			savePort;
	GrafPtr			curPort;
	RgnHandle		theRgn;
	Rect			bounds;
	Point			origin;
	unsigned char	pstr[2];
	Boolean			success;
	GWorldPtr		myWorld;
	short			yMax;
	short			rowBytes;
	DWORD			result = -1;
	
//
//	We currently only support the GGO_BITMAP flavor
//
	if (fuFormat != GGO_BITMAP) return (result);

//
//	Fill in the glyph metrics and calculate the size of the bitmap
//

	if ( BuildGlyphMetrics( uChar, driverData, lpgm, &yMax ) == false )
		return( result );

	GetPort( &savePort );
	curPort = (GrafPtr )driverData->grafPort;
	SetPort( curPort );
//
//	Setup the transform matrix.  The FIXED values that are passed in are 64-bit values,
//	but only the first 32 bits are used like the Macintosh Fixed type.  The m12 and m21
//	values are inverted (*-1) to make the rotation similar to the Windows rotation.
//
	m1.m11 = lpmat2->eM11.fract;
	m1.m12 = -lpmat2->eM12.fract;
	m1.m21 = -lpmat2->eM21.fract;
	m1.m22 = lpmat2->eM22.fract;

	pstr[1] = uChar;
	pstr[0] = 1;
	curPort->pnLoc.h = 100;
	curPort->pnLoc.v = yMax;
	theRgn = NewRgn();
	OpenRgn();
		success = FrameTextTransform( pstr, &m1, &bounds, &origin );
	CloseRgn( theRgn );
	SetPort( savePort );
	if ( success == false )
		return( result );
//
//	Convert the new boundaries and coordinates into the GLYPHMETRICS
//
	lpgm->gmptGlyphOrigin.x = origin.h;
	lpgm->gmptGlyphOrigin.y = origin.v;

	lpgm->gmBlackBoxX = bounds.right - bounds.left;
	lpgm->gmBlackBoxY = bounds.bottom - bounds.top;
	rowBytes = (( lpgm->gmBlackBoxX + 31 ) >> 5 ) << 2;	//	DWORD-aligned width in bytes
	result = lpgm->gmBlackBoxY * rowBytes;

//
//	See if the caller wants some data
//	TODO: Should we bail if the caller asks for less than we have specified previously
//
	if ( cbBuffer && lpvBuffer ) {
		GDHandle		saveGD;
		GWorldPtr		saveGW;
		PixMapHandle	myPixMap;

//
//	Setup a temporary offscreen GWorld big enough for the glyph
//
		if ( NewGWorld( &myWorld, 1, &bounds, 0, 0, useTempMem ) == noErr ) {
			myPixMap = GetGWorldPixMap( myWorld );

//
//	Save the current world and setup the new one
//
			GetGWorld( &saveGW, &saveGD );
			SetGWorld( myWorld, 0 );
			TextFont( driverData->lpFontData->fontID );
			TextFace( driverData->lpFontData->face );
			TextSize( driverData->lpFontData->fontSize );
			LockPixels( GetGWorldPixMap( myWorld ));
//
//	Set the row bytes of the offscreen pixmap to be aligned on a long word boundary
//	Also setting the high bit to indicate that it is a pixmap
//
			(*myPixMap)->rowBytes = 0x8000 + rowBytes;
			EraseRect( &bounds );
//
//	Draw the outside and paint the region into the offscreen world
//
			FrameRgn( theRgn );
			PaintRgn( theRgn );
		
//
//	Now copy from the bitmap from my world to theirs, stretching as necessary
//
//	TODO:	Check the size provided in cbBuffer and don't overflow
			BlockMove( (*myPixMap)->baseAddr, lpvBuffer, result );
			UnlockPixels( GetGWorldPixMap( myWorld ));

//
//	Dispose of the temp world and reset the new old one
//
//	CMA:	Apparently the Mac is trying to be too smart and grabbing the GDevice that
//			is associated with saveGW and setting the GDevice to that.  I expected the
//			SetGWorld call to explicitly use the saveGD param that I pass, but it doesn't.
//			THerefore, I do it manually by calling SetPort() and SetGDevice() seperately.
//			SetGWorld( saveGW, saveGD );
			SetPort((GrafPtr )saveGW );
			SetGDevice( saveGD );
			DisposeGWorld( myWorld );
		}
	}
	DisposeRgn( theRgn );
	return( result );
}
