/* This file contains all the machine-specific code that is not memory-related
(memory-related code can be found in "machinememory.h").  To convert to a
new platform, change the code in this file but do NOT change the API or data
structures unless there is specific comment that you may do so, or it is probably
OK to change all the static/local functions as you see fit.

MS WINDOWS NOTES 4/24/94:

--  The graf_device->machine_var will contain the HWND (main window
for default device, child window for pg_ref-specific). In globals->machine_const
is the original/main window from the app at startup. When PAIGE asks that a
graf_device is to be "used" a GetDC is performed and the GetDC result is placed
in graf_device->machine_ref. When not being used, machine_ref is NULL.

--  The font_info records contains information about the font text face. The
style_info record inself holds the actual font object (created with
CreateFontInd). When the style is "set" the font object is selected. It is
done this way so I create a font object only once for each unique style.

-- The font_info->machine_var data contains output precision, clip precision
and quality info (the element indexes defined in pgTraps.h).

Copyright 1993 by DataPak Software, Inc.  All rights reserved. It is illegal
to remove this notice   */

#include "Paige.h"
#include "pgRegion.h"

#ifdef WINDOWS_PLATFORM

#include <windows.h>
#include <windowsx.h>

#ifdef WIN16_COMPILE
#include "w16nls.h"
//include "olenls.h"

/*
 * fake these out
 */
static void GetCPInfo(short, CPINFO *);
static void GetStringTypeEx(LCID,DWORD,LPCSTR,int,LPWORD);
#endif

#ifdef UNICODE
#define x_GetFontLanguageInfo	GetFontLanguageInfo
#define FirstMultiCharByte(unused1, unused2) FALSE
#else

#ifdef WIN_MULTILINGUAL
#ifdef WIN16_COMPILE	
DWORD WINAPI x_GetFontLanguageInfo( HDC );
#define FirstMultiCharByte(unused, the_char) IsDBCSLeadByte(the_char)
#else
#define x_GetFontLanguageInfo	GetFontLanguageInfo
#define FirstMultiCharByte(code_page, the_char) IsDBCSLeadByteEx(code_page, the_char)
#endif
#else
#define FirstMultiCharByte(unused1, unused2) FALSE
#endif
#endif

#include "pgOSUtl.h"
#include "pgUtils.h"
#include "machine.h"
#include "defprocs.h"
#include "pgDefStl.h"
#include "pgText.h"
#include "pgTxtWid.h"
#include "pgSelect.h"
#include "pgShapes.h"
#include "pgFiles.h"
#include "pgErrors.h"
#include "pgEdit.h"

#include "pgScript.h"
#include "pgUtils.h"
#include "pgExceps.h"
#include "pgGrafx.h"
#include "pgLists.h"
#include "pgSubRef.h"

#include <string.h>

#define WIN_CARET_TIME	1
#define FONT_EXISTS 0x0010

#ifdef UNICODE
#define DISPLAY_DRIVER_NAME L"DISPLAY"
#else
#define DISPLAY_DRIVER_NAME "DISPLAY"
#endif

static void init_global_char (pg_char_ptr global_char, pg_short_t value);
static pg_short_t global_char_to_int (pg_char_ptr global_char);
static short global_char_size (pg_char_ptr global_char);
static void make_graf_device (pg_globals_ptr globals, generic_var port, generic_var machine_ref,
	 	graf_device_ptr device);
static void draw_cursor (paige_rec_ptr pg, t_select_ptr select, short verb);
static void install_machine_font (paige_rec_ptr pg, style_info_ptr style,
		font_info_ptr font, pg_boolean include_offscreen);
static void draw_special_char (paige_rec_ptr pg, pg_char_ptr the_char, co_ordinate_ptr point);
static pg_boolean create_caret (paige_rec_ptr pg, short h_loc, short v_loc, short caret_size);
static void init_def_font_name (paige_rec_ptr pg, font_info_ptr font);
static void set_alternate_pointsize(paige_rec_ptr pg, long new_resolution,
		style_walk_ptr walker);
static short set_invisible_symbol_style (paige_rec_ptr pg);
static void draw_box_style(paige_rec_ptr pg, style_walk_ptr walker,
		draw_points_ptr draw_position);
static void draw_box_blink (paige_rec_ptr pg, style_walk_ptr walker,
		draw_points_ptr draw_position);
static void scale_dc (HDC dc, graf_device_ptr the_device);
static HDC get_unscaled_dc (paige_rec_ptr pg, short PG_FAR *index);
static void restore_scaled_dc (paige_rec_ptr pg, short index);

/* Windows-specific functions: */
int CALLBACK enum_pgfont_proc (LOGFONT far *log, TEXTMETRIC far *tm, int font_type, LPARAM pg_font);
static void get_windows_font_object (paige_rec_ptr pg, style_info_ptr the_style, font_info_ptr font);
static pg_error MemError (void);
static pg_fixed windows_pointsize (pg_fixed ptsize, int resolution);
static pg_fixed windows_screensize (pg_fixed screensize, int resolution);
static HBRUSH build_background_brush (graf_device_ptr device);
static int get_space_extra (HDC hdc, pg_char_ptr text, short text_length,
		pg_char space_char);
static void set_printer_scaling (paige_rec_ptr pg, HDC printer_dc, short use_x, short use_y);
static void unset_printer_scaling (HDC printer_dc);
static short PG_FAR * x_drawing_positions (paige_rec_ptr pg, style_walk_ptr walker,
			pg_char_ptr text, short draw_length, long PG_FAR *end_pen);
static pg_boolean last_char_of_script (style_walk_ptr walker, long offset);

static long set_non_roman_bits (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
		long offset_begin, long offset_end, long char_offset, long mask_bits,
		WORD PG_FAR *ctype_3, pg_short_t PG_FAR *symbol_test, pg_boolean known_leadbyte);
static void init_font_record (paige_rec_ptr pg, font_info_ptr font);
static void init_font_language (font_info_ptr font, HDC hdc);
static long multibyte_character_flags (UINT code_page, pg_char_ptr string, long offset_begin,
			long offset_end, pg_boolean known_leadbyte);
static short extra_print_descent (paige_rec_ptr pg, HDC current_dc, style_info_ptr style);

static pg_boolean	blink = FALSE;
static short		blink_mode = 0;

/* pgMachineInit must initialize all the "global" variables in pg_globals.
This is included here because certain items will be created that are specific
to the platform in question.
WINDOWS NOTE: For Windows, globals->machine_const is used to create a default DC
for whole screen which we are forced to use if app doesn't provide a window
at a critical time.	*/

PG_PASCAL (void) pgMachineInit (pg_globals_ptr globals)
{
	register pg_globals_ptr	 global_vars;
	pg_char					 screen_driver[10];

	global_vars = globals;		  /* Makes for less code */

	global_vars->max_offscreen = MAX_OFFSCREEN;
	global_vars->max_block_size = MAX_TEXTBLOCK;
	global_vars->def_tab_space = DEF_TAB_SPACE;
	global_vars->minimum_line_width = DEF_MIN_WIDTH;
	global_vars->line_wrap_char = CR_CHAR;
	global_vars->soft_line_char = SOFT_CR_CHAR;
	global_vars->tab_char = TAB_CHAR;
	global_vars->soft_hyphen_char = SOFT_HYPHEN;
	global_vars->bs_char = DELETE_CHAR;
	global_vars->ff_char = FF_CHAR;
	global_vars->container_brk_char = CONTAINER_BRK_CHAR;
	global_vars->left_arrow_char = LEFT_ARROW;
	global_vars->right_arrow_char = RIGHT_ARROW;
	global_vars->up_arrow_char = UP_ARROW;
	global_vars->down_arrow_char = DOWN_ARROW;
	global_vars->text_brk_char = TEXT_BRK_CHAR;
	global_vars->fwd_delete_char = FWD_DELETE_CHAR;

	init_global_char(global_vars->hyphen_char, HYPHEN_CHAR);
	init_global_char(global_vars->decimal_char, DECIMAL_CHAR);
	init_global_char(global_vars->cr_invis_symbol, INVIS_CR);
	init_global_char(global_vars->lf_invis_symbol, INVIS_LF);
	init_global_char(global_vars->tab_invis_symbol, INVIS_TAB);
	init_global_char(global_vars->end_invis_symbol, INVIS_END);
	init_global_char(global_vars->cont_invis_symbol, INVIS_BREAK);
	init_global_char(global_vars->pbrk_invis_symbol, INVIS_PBREAK);
	init_global_char(global_vars->space_invis_symbol, INVIS_SPACE);
	init_global_char(global_vars->elipse_symbol, ELIPSE_SYMBOL);
	
	init_global_char(global_vars->flat_single_quote, APOSTROPHE);
	init_global_char(global_vars->flat_double_quote, FLAT_QUOTE_CHAR);
	init_global_char(global_vars->left_single_quote, LEFT_SINGLE_QUOTE);
	init_global_char(global_vars->right_single_quote, RIGHT_SINGLE_QUOTE);
	init_global_char(global_vars->left_double_quote, LEFT_DOUBLE_QUOTE);
	init_global_char(global_vars->right_double_quote, RIGHT_DOUBLE_QUOTE);
	init_global_char(global_vars->unknown_char, UNSUPPORTED_CHAR);
	init_global_char(global_vars->bullet_char, BULLET_CHAR);

	pgFillBlock(screen_driver, sizeof(pg_char) * 10, 0);
	pgBlockMove(DISPLAY_DRIVER_NAME, screen_driver, sizeof(pg_char) * 7);
	global_vars->machine_const = PG_LONGWORD(generic_var)CreateDC((PGSTR)screen_driver, NULL, NULL, NULL);

/* Init the default font, style & para records */

	pgInitDefaultFont(global_vars, &global_vars->def_font);
	pgInitDefaultStyle(global_vars, &global_vars->def_style, &global_vars->def_font);
	pgInitDefaultPar(global_vars, &global_vars->def_par);

	global_vars->def_bk_color.red = global_vars->def_bk_color.green
			= global_vars->def_bk_color.blue = 0xFFFF;
	global_vars->trans_color = global_vars->def_bk_color;

	PG_TRY(global_vars->mem_globals) {
	
	#ifdef WIN16_COMPILE
		global_vars->system_version = GetWinFlags();
	#else
		global_vars->system_version = (long)GetVersion();
	#endif

		global_vars->color_enable = TRUE;	       
		global_vars->font_objects = MemoryAlloc(global_vars->mem_globals, sizeof(font_object_rec), 0, 4);
		global_vars->alpha_widths = MemoryAlloc(global_vars->mem_globals, sizeof(pg_text_int), 0, 128);

/* (no offscreen "buffer" required but initializing the graf_device is necessary) */

		make_graf_device(global_vars, MEM_NULL, MEM_NULL, &globals->offscreen_port);
#ifdef PG_OITC
		global_vars->pg_list = MemoryAlloc(global_vars->mem_globals, sizeof(pg_list_rec), 0, 5);
#endif
	}
	
	PG_CATCH {
		
		pgMachineShutdown(global_vars);
		pgFailure(global_vars->mem_globals, NO_MEMORY_ERR, 0);
	}
	
	PG_ENDTRY;

	global_vars->offscreen_enable = FALSE;
	global_vars->offscreen_exclusion = pgRectToShape(global_vars->mem_globals, NULL);
}


/* pgMachineShutdown must de-allocate anything it created during pgMachineInit */

PG_PASCAL (void) pgMachineShutdown (const pg_globals_ptr globals)
{
	pg_globals_ptr		  global_vars;

	global_vars = globals;		  /* Makes for less code */

	if (global_vars->machine_const)
		DeleteDC((HDC)global_vars->machine_const);

	if (global_vars->font_objects) {
		font_object_ptr			font_objects;
		long					num_fonts;
		
		font_objects = UseMemory(global_vars->font_objects);
		num_fonts = GetMemorySize(global_vars->font_objects);
		
		while (num_fonts) {
			
			DeleteObject((HANDLE)font_objects->the_font);
			
			++font_objects;
			--num_fonts;
		}
		
		UnuseAndDispose(global_vars->font_objects);
	}
	
	pgCloseDevice(global_vars, &global_vars->offscreen_port);
	DisposeNonNilMemory(global_vars->offscreen_exclusion);
	DisposeNonNilMemory(global_vars->character_sets);
	DisposeNonNilMemory(global_vars->alpha_widths);
	DisposeNonNilMemory(global_vars->pg_list);
}



/* pgInitDefaultDevice sets up a device to use as a default. In Mac, for instance,
this would be simply the current port */

PG_PASCAL (void) pgInitDefaultDevice (const pg_globals_ptr globals, graf_device_ptr device)
{
	HWND		    focus_window;
	
	if (focus_window = GetFocus())
		pgInitDevice(globals, PG_LONGWORD(generic_var) focus_window, 0, device);
}


/* pgInitDevice sets up a Paige graf_device. Although the code in this file
is machine-dependent, we can hopefully use this same API for all platforms. The
idea is to pass <whatever is appropriate> in the_port and/or machine_var and
let this machine-specific function initialize a generic Paige graf_device.
FOR MACINTOSH: the_port is a GrafPtr. */

PG_PASCAL (void) pgInitDevice (const pg_globals_ptr globals, const generic_var the_port,
		long machine_ref, graf_device_ptr device)
{
	make_graf_device(globals, the_port, machine_ref, device);
}


/* pgCloseDevice disposes everything created for pgInitDevice.  */

PG_PASCAL (void) pgCloseDevice (const pg_globals_ptr globals, const graf_device_ptr device)
{
	if (device->graf_stack)
		DisposeMemory(device->graf_stack);

	if (device->clip_rgn)
		pgDisposeRgn(device->clip_rgn);

	if (device->scroll_rgn)
		pgDisposeRgn(device->scroll_rgn);
}



/* pgInsertQuery is a version 1.3 function, designed mostly for multilingual input. When PAIGE
is asked to insert a key with key_insert_mode, this hook is called. This hook must return
the insertion mode to use. For example, if another key is required and the current char should
be buffered then key_buffer_mode can be returned. */

PG_PASCAL (short) pgInsertQuery (paige_rec_ptr pg, pg_char_ptr the_char, short charsize)
{
	MSG				msg;
	HWND			win;
	
	win = (HWND)pg->port.machine_var;

	if (win) {
	
		if (!PeekMessage(&msg, win, WM_CHAR, WM_CHAR, PM_NOREMOVE))
			return	key_insert_mode;
		if (PeekMessage(&msg, win, WM_KEYDOWN, WM_KEYDOWN, PM_NOREMOVE))
			return	key_insert_mode;
		
		return	key_buffer_mode;
	}

	return	(short)key_insert_mode;
}


/* pgCharClassProc gets called when parsing chars to determine the subset type. Usually this
refers to multilingual double byte chars so highlighting will group all similar types. */

PG_PASCAL (pg_word) pgCharClassProc (paige_rec_ptr pg, pg_char_ptr the_char, short charsize,
		style_info_ptr style, font_info_ptr font)
{
#ifdef WIN_MULTILINGUAL
	WORD			types[4];
	
	GetStringTypeEx((LCID)font->language, CT_CTYPE3, (PGSTR)the_char, (int)charsize, types);
	types[0] &= (~(C3_HALFWIDTH | C3_FULLWIDTH));

	if (charsize == 1)
		types[0] |= C3_HALFWIDTH;
	else
		types[0] |= C3_FULLWIDTH;

	return	(WORD)(types[0] & (C3_KATAKANA | C3_HIRAGANA | C3_IDEOGRAPH | C3_HALFWIDTH | C3_FULLWIDTH));
#endif
	return	0;
}

/* DEFAULT HIGHLIGHT DRAW FUNCTION. This is the internal hook used to display
highlight. It is obviously machine-dependent. */

PG_PASCAL (void) pgDrawHiliteProc (paige_rec_ptr pg, shape_ref rgn)
{
	Rect					    piece_of_shape;
	rectangle				    vis_bounds, scaled_r, container;
	co_ordinate				    scroll_adjust, repeat_offset;
	register rectangle_ptr	  	r_ptr;
	register long			    r_qty;
	long					    container_proc_refcon;
	pg_short_t				    last_container, real_container;
	HDC						    hdc = pgGetPlatformDevice(&pg->port);
	
	SetMapMode(hdc, MM_TEXT);
	r_ptr = UseMemory(rgn);
    
    pg->scale_factor.scale = pg->port.scale.scale;
    
	scroll_adjust = pg->scroll_pos;
	pgNegatePt(&scroll_adjust);
	pgAddPt(&pg->port.origin, &scroll_adjust);

	pgShapeBounds(pg->vis_area, &vis_bounds);
	pgOffsetRect(&vis_bounds, pg->port.origin.h, pg->port.origin.v);
	
	if (pg->flags & SCALE_VIS_BIT)
		pgScaleRect(&pg->port.scale, NULL, &vis_bounds);

//	pgScaleLong(-pg->port.scale.scale, vis_bounds.top_left.h, &vis_bounds.bot_right.h); 
// 	pgScaleLong(-pg->port.scale.scale, vis_bounds.top_left.v, &vis_bounds.bot_right.v); 
	
	pgInsetRect(&vis_bounds, -8, -8);

	container_proc_refcon = 0;
	
	real_container = pgGetSectWrapRect(pg, &r_ptr[1], &last_container, &repeat_offset);
	
	GetMemoryRecord(pg->wrap_area, real_container + 1, &container);
	pgOffsetRect(&container, repeat_offset.h, repeat_offset.v);

	pgCallContainerProc(pg, last_container, &scroll_adjust,
		clip_container_verb,  &pg->port.scale, &container_proc_refcon); 
	pgCallContainerProc(pg, last_container, &scroll_adjust,
		will_draw_verb,  &pg->port.scale, NULL);

	for (r_qty = GetMemorySize(rgn) - 1; r_qty; --r_qty) {
		
		++r_ptr;
		
		if (!pgSectRect(r_ptr, &container, NULL)) {
			pg_short_t		      new_container;
			
			real_container = pgGetSectWrapRect(pg, r_ptr, &new_container, &repeat_offset);
			
			if (new_container != last_container) {
				
				last_container = new_container;
				GetMemoryRecord(pg->wrap_area, real_container + 1, &container);
				pgOffsetRect(&container, repeat_offset.h, repeat_offset.v);
				
				pgCallContainerProc(pg, last_container, &scroll_adjust,
					clip_container_verb,  &pg->port.scale, &container_proc_refcon); 
				pgCallContainerProc(pg, last_container, &scroll_adjust,
					will_draw_verb,  &pg->port.scale, NULL);
			}
		}

		pgScaleRectToRect(&pg->scale_factor, r_ptr, &scaled_r, &scroll_adjust);

		if ((scaled_r.top_left.v < vis_bounds.bot_right.v)
				&& (scaled_r.bot_right.v > vis_bounds.top_left.v)) {
			
			RectangleToRect(&scaled_r, NULL, &piece_of_shape);
			InvertRect(hdc, &piece_of_shape);
		}
	}
	
	pg->scale_factor.scale = 0;
	UnuseMemory(rgn);
	
	if (pg->port.scale.scale)
		scale_dc(hdc, &pg->port);

	pgCallContainerProc(pg, last_container, NULL, unclip_container_verb,  NULL, &container_proc_refcon);
	pgReleasePlatformDevice(&pg->port);
}



/* DEFAULT HIGHLIGHT DRAW THE CUSOR ("caret"). It is obviously machine-dependent.
NOTE: The correct port is set. */

PG_PASCAL (void) pgDrawCursorProc (paige_rec_ptr pg, t_select_ptr select,
		short verb)
{
	if ((pg->flags & NO_EDIT_BIT) || !pg->port.machine_var)
		return;

	switch (verb) {

		case toggle_cursor:    
		case toggle_cursor_idle:
			if (!(pg->flags & CARET_CREATED_BIT))
				draw_cursor(pg, select, verb);
			break;

		case hide_cursor:
		case deactivate_cursor:
			if (pg->flags & CARET_BIT) {
			
				draw_cursor(pg, select, verb);
				pg->flags &= (~CARET_BIT);
			}
			break;

		case activate_cursor:
			draw_cursor(pg, select, verb);
			break;

		case show_cursor:
			draw_cursor(pg, select, verb);
			pg->flags |= CARET_BIT;
			break;

		case update_cursor:
			if ((pg->flags & INVALID_CURSOR_BIT) && (pg->flags & CARET_CREATED_BIT)) {
				
				DestroyCaret();
				pg->flags &= (long)(~(CARET_BIT | CARET_CREATED_BIT));
				draw_cursor(pg, select, update_cursor);
			}
			else
			if (pg->flags & CARET_BIT)
				draw_cursor(pg, select, verb);
			break;
		
		case restore_cursor:
			if ((pg->flags & INVALID_CURSOR_BIT) && (pg->flags & CARET_CREATED_BIT)) {

				DestroyCaret();
				pg->flags &= (long)(~(CARET_BIT | CARET_CREATED_BIT));
			}

			if (!(pg->flags & CARET_BIT))
				draw_cursor(pg, select, verb);
			
			break;

		case compute_cursor:
			draw_cursor(pg, select, verb);
			break;
	}       
}



/* DEFAULT FUNCTION TO INITIALIZE A STYLE RECORD. It can be somewhat machine-
dependent. */

PG_PASCAL (void) pgStyleInitProc (paige_rec_ptr pg, style_info_ptr style,
			font_info_ptr font)
{
	register			style_info_ptr  the_style;
	HDC			      	hdc;
	TEXTMETRIC	        tm;
	short	    		underline_extra, save_index, super_or_sub_extra;
	
	the_style = style;
	underline_extra = 0;

	hdc = get_unscaled_dc(pg, &save_index);	
	get_windows_font_object(pg, the_style, font);
	the_style->procs.install(pg, the_style, font, NULL, FALSE);
	GetTextMetrics(hdc, &tm);
 
	the_style->ascent       = (short)tm.tmAscent;
	the_style->descent      = (short)tm.tmDescent;
	
	if (!(pg->flags & NO_DEFAULT_LEADING))
		the_style->leading      = (short)tm.tmExternalLeading; //+ tm.tmExternalLeading;
	else
		the_style->leading = 0;

	the_style->right_overhang = (long) tm.tmOverhang;

	restore_scaled_dc(pg, save_index);

	if (super_or_sub_extra = the_style->styles[superscript_var]) {
		
		if (the_style->shift_verb == percent_of_style)
			super_or_sub_extra = ((the_style->ascent + the_style->descent) * super_or_sub_extra) / 100;

		the_style->ascent += super_or_sub_extra;
	}

	if (font->char_type & (RIGHT_FONT_FLAG | MULTIBYTE_FLAG))
		the_style->class_bits |= NO_SMART_DRAW_BIT;
	else
		the_style->class_bits &= (~NO_SMART_DRAW_BIT);

	if (font->char_type & RIGHT_FONT_FLAG)
		the_style->class_bits |= RIGHTLEFT_BIT;
	else
		the_style->class_bits &= (~RIGHTLEFT_BIT);
	
	if (super_or_sub_extra = the_style->styles[subscript_var]) {
		
		if (the_style->shift_verb == percent_of_style)
			super_or_sub_extra = ((the_style->ascent + the_style->descent) * super_or_sub_extra) / 100;
		
		the_style->descent += super_or_sub_extra;
	}

	if (pg->flags & LINE_EDITOR_BIT)
		underline_extra = 0;
	else
	if (the_style->styles[dbl_underline_var])
		underline_extra += 2;
	
	if (the_style->styles[overline_var])
		the_style->ascent += underline_extra;
	else
		the_style->leading += underline_extra;
}


/* DEFAULT FONT/STYLE INSTALL PROC. Obviously machine-dependent.
Added 9/6/95 "composite_style" which returns superimpose composite if non-NULL. */


PG_PASCAL (void) pgInstallFont (paige_rec_ptr pg, style_info_ptr the_style,
		font_info_ptr the_font, style_info_ptr composite_style, short include_offscreen)
{
	pg_globals_ptr	  			globals;
	graf_device_ptr	  			old_device;
	style_info					imposed_style;
	font_info					imposed_font;
	register style_info_ptr		style;
	register font_info_ptr		font;

	globals = pg->globals;

	style = the_style;
	font = the_font;
	
	if (style->styles[super_impose_var]) {
		
		pgStyleSuperImpose(pg, style, &imposed_style, &imposed_font);
		
		style = &imposed_style;
		font = &imposed_font;
		get_windows_font_object(pg, &imposed_style, &imposed_font);
	}
	else
	if (style->styles[relative_point_var] || style->styles[nested_subset_var]) {
		long			point_size;

		pgBlockMove(style, &imposed_style, sizeof(style_info));
		point_size = pgComputePointSize(pg, &imposed_style);
		point_size <<= 16;
		imposed_style.point = point_size;
		imposed_style.styles[relative_point_var] = imposed_style.styles[nested_subset_var] = 0;
		imposed_style.procs.init(pg, &imposed_style, the_font);
		style = &imposed_style;
	}

    old_device = globals->current_port;
    globals->current_port = (graf_device_ptr) &pg->port;
	install_machine_font(pg, style, font, include_offscreen);
    globals->current_port = old_device;

	if (include_offscreen) {
		if (globals->offscreen_port.machine_ref) {

			old_device = globals->current_port;
			globals->current_port = (graf_device_ptr) &globals->offscreen_port;
			install_machine_font(pg, style, font, FALSE);
			globals->current_port = old_device;
		}
	}
	
	if (composite_style)
		pgBlockMove(style, composite_style, sizeof(style_info));
}


/* DEFAULT DELETE STYLE PROC. For Mac and Windows, this does nothing. WARNING: pg can
be NULL if called from Undo functions! */

PG_PASCAL (void) pgDeleteStyleProc (paige_rec_ptr pg, pg_globals_ptr globals,
		short reason_verb, format_ref all_styles, style_info_ptr style)
{

}


/* DEFAULT DUPLICATE STYLE PROC. For Mac and Windows, this does nothing.
WARNING: pg can be NULL if called from Undo functions!     */

PG_PASCAL (void) pgDupStyleProc (paige_rec_ptr src_pg, paige_rec_ptr target_pg,
		short reason_verb, format_ref all_styles, style_info_ptr style)
{

}


/* pgSaveStyleProc is the default style that gets called just prior to saving
it to a file. For Macintosh this does nothing. For Windows the machine_var field
is zerod out (because it is a handle). */

PG_PASCAL (void) pgSaveStyleProc (paige_rec_ptr pg, style_info_ptr style_to_save)
{

}



/* DEFAULT FUNCTION TO MEASURE TEXT WIDTHS. It is obviously machine-dependent. 
Note: The current font, style, etc. is pre-set before this call. The types
parameter is a pointer to an array of shorts that should be set to character
type bits (as in char_info proc).  HOWEVER, only control char bits should be
set such as CR, TAB, etc.  NOTE: types can be NULL.
Changes 2/15/94 -- added measure_verb param which indicates the type of output
wanted (see enums in Paige.h). Also removed style pointer and font pointer,
replaced with a single style_walk_ptr param.
Added 4/14/94: if scale_widths is TRUE the result(s) are to be scaled.  */

PG_PASCAL (void) pgMeasureProc (paige_rec_ptr pg, style_walk_ptr walker,
		pg_char_ptr data, long length, pg_short_t slop, long PG_FAR *positions,
		short PG_FAR *types, short measure_verb, long current_offset, pg_boolean scale_widths,
		short call_order)
{
	register short		  	PG_FAR *each_type;
	register int	    	PG_FAR *int_positions;
	register pg_char_ptr    text;
	register long		   	PG_FAR *long_positions;
	register pg_short_t	   	the_byte;
	pg_char_ptr			    measure_data, original_data;
	short PG_FAR		    *the_types;
	long PG_FAR			    *measure_positions;
	memory_ref			    transliterate_ref;
	style_info_ptr		  	style, original_style;
	font_info_ptr		   	font;
	short				   	char_bytes, right_to_left, soft_hyphens_exist;
	short				   	system_just, done_types;
	long				    measure_length, position_bias, extra_just, scale_slop, use_slop;
	long				    total_length, position_ctr, num_spaces;
	pg_fixed				space_extra_width, space_accumulation;
	pg_boolean				small_caps_compensate;

	style = original_style = walker->cur_style;
	font = walker->cur_font;
	the_types = types;
	
	if (style->class_bits & NON_TEXT_BITS) {
		
		for (measure_length = 0, long_positions = positions;
				measure_length <= length; ++measure_length)
			long_positions[measure_length] = *long_positions;
		
		if (the_types)
			pgFillBlock(the_types, length * sizeof(short), 0);

		return;
	}
	
	if (transliterate_ref = pgConvertTextCaps(pg, style, data, length))
		measure_data = UseMemory(transliterate_ref);
	else
		measure_data = data;
	
	original_data = data;

	total_length = length;
	measure_positions = positions;
	
	use_slop = (long)slop;

// Determine if we need to compensate for full-justify, small caps style:

	small_caps_compensate = (pg_boolean)((original_style->styles[small_caps_var] != 0) && (slop != 0));
	
	if (small_caps_compensate) {
		
		if (!(num_spaces = get_space_extra((HDC)pg->port.machine_ref, data, (short)length, 0)))
			small_caps_compensate = FALSE;
		else {
		
			space_extra_width = (pg_fixed)pgFixedRatio((short)slop, (short)num_spaces);
			space_accumulation = 0;
		}
	}

	while (total_length) {
		
		measure_length = total_length;
		walker->cur_style = style = original_style;

		if (style->styles[small_caps_var]) {
			long		    info_flags;
			
			measure_length = pgSameCaseLength(pg, walker, original_data,
					current_offset, total_length, &info_flags);
			
			if (!info_flags) {
				
				style = UseMemoryRecord(pg->t_formats, style->small_caps_index,
						0, FALSE);
				walker->cur_style = style;
			}
			
			style->procs.install(pg, style, walker->cur_font, &walker->superimpose, FALSE);

			if (small_caps_compensate) {
				
				if ((num_spaces = get_space_extra((HDC)pg->port.machine_ref, measure_data, (short)measure_length, 0)) > 0) {

					pg_fixed			fractional_slop;
					
					fractional_slop = (pg_fixed)num_spaces;
					fractional_slop <<= 16;
					fractional_slop = pgMultiplyFixed(fractional_slop, space_extra_width);
					space_accumulation += fractional_slop;
					use_slop = HIWORD(space_accumulation);
					space_accumulation &= 0x0000FFFF;
				}
				else
					use_slop = 0;
			}
		}

		int_positions = (int PG_FAR *) measure_positions;
		position_bias = *measure_positions;
		scale_slop = use_slop;
		extra_just = 0;
		
		if (scale_slop)
			num_spaces = get_space_extra((HDC)pg->port.machine_ref, measure_data, (short)measure_length, 0);
		else
			num_spaces = 0;

		pgMeasureText(pg, measure_verb, measure_data, measure_length,
					scale_slop - extra_just, num_spaces, int_positions, walker);

		right_to_left = (font->char_type & RIGHT_FONT_FLAG);
		system_just = pgSystemDirection(pg->globals);

		long_positions = measure_positions;
		position_ctr = measure_length + 1;
		long_positions += position_ctr;
		int_positions += position_ctr;
	
		*(--long_positions) = *(--int_positions);
		*long_positions += position_bias;
		--position_ctr;

		if (each_type = the_types) {
		
			each_type += position_ctr;
			text = original_data + position_ctr;
			char_bytes = style->char_bytes;
		}
	
		done_types = 0;
		soft_hyphens_exist = FALSE;
	
		while (position_ctr) {
			*(--long_positions) = *(--int_positions);
			*long_positions += position_bias;
	
			if (each_type) {
	
				++done_types;
	
				the_byte = *(--text);
				*(--each_type) = 0;
	
				if (!char_bytes) {
					
					if (the_byte == ' ')
						*each_type = BLANK_BIT;
					else
					if (the_byte < ' ') {
						pg_globals_ptr	  globals;
						
						globals = pg->globals;
		
						*each_type |= CTL_BIT;
			
						if (the_byte == globals->line_wrap_char)
							*each_type |= PAR_SEL_BIT;
						else
						if (the_byte == globals->soft_line_char)
							*each_type |= LINE_SEL_BIT;
						else
						if (the_byte == globals->tab_char)
							*each_type |= TAB_BIT;
						else
						if (the_byte == globals->container_brk_char)
							*each_type |= CONTAINER_BRK_BIT;
						else
						if (the_byte == globals->ff_char)
							*each_type |= PAGE_BRK_BIT;
						else
						if (the_byte == globals->soft_hyphen_char) {
		
							*each_type |= SOFT_HYPHEN_BIT;
							soft_hyphens_exist = TRUE;
						}
					}
				}
			}
	
			--position_ctr;
		}
		
		if (soft_hyphens_exist) {
			long	    hyphen_width, older_position;
			HDC		     hdc = pgGetPlatformDevice(&pg->port);

			hyphen_width = MEASURE_SYMBOL(pg->globals->hyphen_char);
			pgReleasePlatformDevice(&pg->port);
			position_bias = 0;
	
			while (done_types) {
				
				older_position = *long_positions;
				*long_positions += position_bias;

				if (*each_type & SOFT_HYPHEN_BIT)				       
					position_bias += older_position - long_positions[1] + hyphen_width;
				
				++long_positions;
				++each_type;
				--done_types;
			}
		}
	
		if ( right_to_left && (system_just >= 0) && (measure_verb != measure_width_locs)) {
			
			long		    trailing_pos;
			
			trailing_pos = *measure_positions;
			*measure_positions = measure_positions[measure_length];
			
			if (call_order == -1)
				measure_positions[measure_length] = trailing_pos;
		}
	
		if (scale_widths == COMPENSATE_SCALE) {
			pg_fixed		scaling;
			long		    origin;
	
			long_positions = measure_positions;
			scaling = -pg->scale_factor.scale;      /* To upscale */
			origin = *long_positions;
	
			for (position_ctr = measure_length + 1; position_ctr; --position_ctr, ++long_positions)
				pgScaleLong(scaling, origin, long_positions);
		}
		
		measure_positions += measure_length;
		original_data += measure_length;
		
		if (the_types)
			the_types += measure_length;

		measure_data += measure_length;
		total_length -= measure_length;
	}

	if (transliterate_ref)
		UnuseMemory(transliterate_ref);

	walker->cur_style = original_style;
}



/* DEFAULT FUNCTION RETURN INFO ABOUT A CHARACTER. It can be somewhat machine-
dependent so we put it in "machinespecific.c". NOTE: Unlike pgDrawProc the
font, style, etc. is not necessarily pre-set.
Changes 1/20/94 -- added parameter "mask_bits" that informs the function which
bits you care about. This greatly increases the potential performance particularly
when only a few character attributes need to be checked. 
Also added, "global_offset" which is the absolute offset base for which local_offset
is relative to (global_offset is usually the text_block.begin value).
Also function result is now a long. */

#ifdef UNICODE
PG_PASCAL (long) pgCharInfoProc (paige_rec_ptr pg, style_walk_ptr style_walker,
		pg_char_ptr data, long block_offset, long offset_begin, long offset_end, long char_offset,
		long mask_bits)
{
	register		style_walk_ptr  walker;
	register		pg_char		 	the_char;
	register		long		    result;
	register		pg_globals_ptr  globals;
	long						    real_break_bit, class_bits, proc_char_offset;
	pg_boolean					    font_non_roman;
	pg_short_t					    decimal_char, hyphen_char;
	pg_short_t					    symbol_check;
	short						    char_bytes;

	walker = style_walker;

	if ((pg->flags & NO_HIDDEN_TEXT_BIT) && walker->cur_style->styles[hidden_text_var])
		return  0;

	the_char = data[char_offset];

	font_non_roman = (pg_boolean)((walker->cur_font->char_type & NON_ROMAN_FLAG) != 0);
	proc_char_offset = char_offset;

	if (!(mask_bits & NON_MULTIBYTE_BITS))
		if (the_char < 256)
			return	0;

	class_bits = walker->cur_style->class_bits;
	globals = pg->globals;
	
	symbol_check = the_char;

	if (mask_bits & DECIMAL_CHAR_BIT)
		decimal_char = global_char_to_int(globals->decimal_char);
	else
		decimal_char = 0;

	if (mask_bits & WORD_BREAK_BIT)
		hyphen_char = global_char_to_int(globals->hyphen_char);
	else
		hyphen_char = 0;

	result = 0;

	real_break_bit = WORD_BREAK_BIT;
	if (class_bits & CANNOT_BREAK)
		if (walker->current_offset != walker->prev_style_run->offset)
			real_break_bit = 0;
	
	char_bytes = walker->cur_style->char_bytes + 1;

	if ((char_bytes > 1) || (class_bits & NON_TEXT_BITS)) {
		long	    distance_in;
		short	   byte_remainder;
		
		/* Multi-byte char or non-text chars */
		
		distance_in = walker->current_offset - walker->prev_style_run->offset;
		byte_remainder = (short)distance_in;
		if (!(byte_remainder %= char_bytes))
			result |= (FIRST_HALF_BIT | WORD_BREAK_BIT | WORD_SEL_BIT);
		else {
			
			if (byte_remainder == (char_bytes - 1))
				result |= (LAST_HALF_BIT | WORD_BREAK_BIT | WORD_SEL_BIT
						| INCLUDE_SEL_BIT | INCLUDE_BREAK_BIT);
			else
				result |= MIDDLE_CHAR_BIT;
		}

		result |= NON_TEXT_BIT;
	}
	else
	if (the_char > 255) {
		long			next_char_offset;
		WORD			char_type, second_char_type;

		if (font_non_roman)
			result |= NON_ROMAN_BIT;

		result |= set_non_roman_bits (pg, walker, data, offset_begin, offset_end, char_offset,
				mask_bits, &char_type, &symbol_check, FALSE);

		if (char_type & C3_SYMBOL)
			result |= SYMBOL_BIT;
	
		if (result & BLANK_BIT)
			result |= (WORD_SEL_BIT);

		result |= WORD_BREAK_BIT;
		next_char_offset = char_offset + 1;

		if ((mask_bits & (WORD_BREAK_BIT | WORD_SEL_BIT)) && next_char_offset < offset_end) {
			long		result2;

			result2 = set_non_roman_bits (pg, walker, data, offset_begin, offset_end, next_char_offset,
				mask_bits, &second_char_type, NULL, FALSE);
			
			if (data[next_char_offset] < 256)
				result |= WORD_BREAK_BIT | INCLUDE_BREAK_BIT | WORD_SEL_BIT | INCLUDE_SEL_BIT;
			else {

				if (char_type != second_char_type)
					result |= WORD_SEL_BIT | INCLUDE_SEL_BIT;
			}
		}
	}
	else {
	
		if (mask_bits == BLANK_BIT) {
			
			if (the_char <= ' ')
				result |= BLANK_BIT;
		}
		else
		if (mask_bits & (~MULTICHAR_BITS)) {
	
			if (the_char <= ' ') {
				
				if (the_char < ' ') {
				
					result |= CTL_BIT;
					
					if (mask_bits & (PAR_SEL_BIT | LINE_SEL_BIT | TAB_BIT | CONTAINER_BRK_BIT | PAGE_BRK_BIT | INVIS_ACTION_BIT)) {
					
						if (the_char == globals->line_wrap_char)
							result |= (PAR_SEL_BIT | BLANK_BIT);
						else
						if (the_char == globals->soft_line_char)
							result |= (LINE_SEL_BIT | BLANK_BIT);
						else
						if (the_char == globals->tab_char)
							result |= (TAB_BIT | BLANK_BIT);
						else
						if (the_char == globals->container_brk_char)
							result |= (CONTAINER_BRK_BIT | BLANK_BIT);
						else
						if (the_char == globals->ff_char)
							result |= (PAGE_BRK_BIT | BLANK_BIT);
						else
							result |= INVIS_ACTION_BIT;
					}
				}
				else
					result |= BLANK_BIT;
	
				if (the_char == globals->soft_hyphen_char) {
					
					result &= (~(WORD_BREAK_BIT | WORD_SEL_BIT));
					result |= (BLANK_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT | SOFT_HYPHEN_BIT);
				}
				else
					result |=  (WORD_BREAK_BIT | WORD_SEL_BIT);
			}
			else {
				
				if (the_char == globals->fwd_delete_char)
					result |= INVIS_ACTION_BIT;
				else
 				if (font_non_roman && the_char > 255 && (mask_bits & (WORD_SEL_BIT | WORD_BREAK_BIT)))
					result |= (WORD_BREAK_BIT | WORD_SEL_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT);
				else
				if (block_offset != NO_BLOCK_OFFSET && last_char_of_script(walker, char_offset + block_offset))
					result |= (WORD_BREAK_BIT | WORD_SEL_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT);
				else
				if ((the_char < '0') && (the_char != global_char_to_int(globals->flat_single_quote)) && (the_char != decimal_char)
						&& (the_char != hyphen_char))
					result |= (WORD_SEL_BIT | INCLUDE_BREAK_BIT);
				else
				if ((the_char == global_char_to_int(globals->left_double_quote))
					|| (the_char == global_char_to_int(globals->right_double_quote)))
					result |= (WORD_SEL_BIT | INCLUDE_BREAK_BIT);
				else {
					
					if (mask_bits & (WORD_BREAK_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT | WORD_SEL_BIT)) {
						
						result |= INCLUDE_BREAK_BIT;
						
						if (the_char == hyphen_char)
							result |= WORD_BREAK_BIT;
	
						if (the_char == decimal_char)
							result |= WORD_SEL_BIT;
						else
						if (the_char < 'A' && the_char > '9')
							result |= WORD_SEL_BIT;
						else
						result |= INCLUDE_SEL_BIT;
					}
				}
			}
	
			if (mask_bits & LONG_FORM_INFO) {
				if ((the_char >= 'A') && (the_char <= 'Z'))
					result |= UPPER_CASE_BIT;
				if ((the_char >= '0') && (the_char <= '9'))
					result |= NUMBER_BIT;
				else
				if ((the_char > 0x20) && (the_char < 0x40))
					result |= PUNCT_NORMAL_BIT;
			}
		}
	}  // End Roman char check

	if (symbol_check == decimal_char)
		result |= DECIMAL_CHAR_BIT;
		
	if (mask_bits & QUOTE_BITS) {

		if (global_char_to_int(globals->flat_single_quote) == symbol_check)
			result |= (FLAT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->flat_double_quote) == symbol_check)
			result |= (FLAT_QUOTE_BIT);

		if (global_char_to_int(globals->left_single_quote) == symbol_check)
			result |= (LEFT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->right_single_quote) == symbol_check)
			result |= (RIGHT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->left_double_quote) == symbol_check)
			result |= (LEFT_QUOTE_BIT);

		if (global_char_to_int(globals->right_double_quote) == symbol_check)
			result |= (RIGHT_QUOTE_BIT);
	}

	if (mask_bits & LOWER_CASE_BIT)
		if (!(result & (QUOTE_BITS | UPPER_CASE_BIT | PUNCT_NORMAL_BIT | NUMBER_BIT | SYMBOL_BIT)))
			result |= LOWER_CASE_BIT;

#ifdef PG_SUBSETS
	if (the_char == SUBREF_CHAR)
		result &= (~BLANK_BIT); 
#endif

	if (mask_bits & WORDBREAK_PROC_BITS)
		result = pg->procs.wordbreak_proc(pg, &data[char_offset], char_bytes, walker->cur_style,
				walker->cur_font, result);

	return  result & mask_bits;
}

#else
// Non-Unicode:

PG_PASCAL (long) pgCharInfoProc (paige_rec_ptr pg, style_walk_ptr style_walker,
		pg_char_ptr data, long block_offset, long offset_begin, long offset_end, long char_offset,
		long mask_bits)
{
	register		style_walk_ptr  walker;
	register		pg_char		 	the_char;
	register		long		    result;
	register		pg_globals_ptr  globals;
	long						    real_break_bit, class_bits, proc_char_offset;
	pg_boolean					    non_roman, font_non_roman;
	pg_short_t					    decimal_char, hyphen_char;
	pg_short_t					    symbol_check;
	short						    char_bytes;

	walker = style_walker;

	if ((pg->flags & NO_HIDDEN_TEXT_BIT) && walker->cur_style->styles[hidden_text_var])
		return  0;

	the_char = data[char_offset];

	non_roman = (((walker->cur_font->char_type & NON_ROMAN_FLAG) != 0) && (mask_bits & NON_CTL_CHAR_BITS));

	font_non_roman = non_roman;
	proc_char_offset = char_offset;

	if (!(mask_bits & NON_MULTIBYTE_BITS))
		if (!non_roman)
			return	0;

	class_bits = walker->cur_style->class_bits;
	globals = pg->globals;
	
	symbol_check = the_char;

	if (mask_bits & DECIMAL_CHAR_BIT)
		decimal_char = global_char_to_int(globals->decimal_char);
	else
		decimal_char = 0;

	if (mask_bits & WORD_BREAK_BIT)
		hyphen_char = global_char_to_int(globals->hyphen_char);
	else
		hyphen_char = 0;

	result = 0;

	real_break_bit = WORD_BREAK_BIT;
	if (class_bits & CANNOT_BREAK)
		if (walker->current_offset != walker->prev_style_run->offset)
			real_break_bit = 0;
	
	char_bytes = walker->cur_style->char_bytes + 1;

	if ((char_bytes > 1) || (class_bits & NON_TEXT_BITS)) {
		long	    distance_in;
		short	   byte_remainder;
		
		/* Multi-byte char or non-text chars */
		
		distance_in = walker->current_offset - walker->prev_style_run->offset;
		byte_remainder = (short)distance_in;
		if (!(byte_remainder %= char_bytes))
			result |= (FIRST_HALF_BIT | WORD_BREAK_BIT | WORD_SEL_BIT);
		else {
			
			if (byte_remainder == (char_bytes - 1))
				result |= (LAST_HALF_BIT | WORD_BREAK_BIT | WORD_SEL_BIT
						| INCLUDE_SEL_BIT | INCLUDE_BREAK_BIT);
			else
				result |= MIDDLE_CHAR_BIT;
		}

		result |= NON_TEXT_BIT;
	}
	else
	
	if (non_roman) {
		WORD			first_char_type;

		result |= NON_ROMAN_BIT;

		result |= set_non_roman_bits (pg, walker, data, offset_begin, offset_end, char_offset,
				mask_bits, &first_char_type, &symbol_check, FALSE);

		if (result & (FIRST_HALF_BIT | LAST_HALF_BIT)) {  // If multibyte char...
			long			next_char_offset;
			WORD			second_char_type;			
			char_bytes = 2;

			if (first_char_type & C3_SYMBOL)
				result |= SYMBOL_BIT;
		
			if (result & BLANK_BIT) {

				result |= WORD_SEL_BIT;

				if (result & LAST_HALF_BIT)
					result |= INCLUDE_SEL_BIT;
 			}
 				
			result |= WORD_BREAK_BIT;
			next_char_offset = char_offset + 2;

			if ((result & FIRST_HALF_BIT) && (mask_bits & (WORD_BREAK_BIT | WORD_SEL_BIT)) && next_char_offset < offset_end) {
				long		result2;

				result2 = set_non_roman_bits (pg, walker, data, offset_begin, offset_end, next_char_offset,
					FIRST_HALF_BIT | LAST_HALF_BIT, &second_char_type, NULL, TRUE);
				
				if ((result2 & (FIRST_HALF_BIT | LAST_HALF_BIT)) == 0)
					result |= WORD_BREAK_BIT | INCLUDE_BREAK_BIT | WORD_SEL_BIT | INCLUDE_SEL_BIT;
				else {

					if (first_char_type != second_char_type)
						result |= WORD_SEL_BIT | INCLUDE_SEL_BIT;
				}
			}
		}
		else  {

			non_roman = FALSE;   // Forces this to fall through to ASCII test.
			result = 0;
		}
	}

	if (!non_roman) {
	
		if (mask_bits == BLANK_BIT) {
			
			if (the_char <= ' ')
				result |= BLANK_BIT;
		}
		else
		if (mask_bits & (~MULTICHAR_BITS)) {
	
			if (the_char <= ' ') {
				
				if (the_char < ' ') {
				
					result |= CTL_BIT;
					
					if (mask_bits & (PAR_SEL_BIT | LINE_SEL_BIT | TAB_BIT | CONTAINER_BRK_BIT | PAGE_BRK_BIT | INVIS_ACTION_BIT)) {
					
						if (the_char == globals->line_wrap_char)
							result |= (PAR_SEL_BIT | BLANK_BIT);
						else
						if (the_char == globals->soft_line_char)
							result |= (LINE_SEL_BIT | BLANK_BIT);
						else
						if (the_char == globals->tab_char)
							result |= (TAB_BIT | BLANK_BIT);
						else
						if (the_char == globals->container_brk_char)
							result |= (CONTAINER_BRK_BIT | BLANK_BIT);
						else
						if (the_char == globals->ff_char)
							result |= (PAGE_BRK_BIT | BLANK_BIT);
						else
							result |= INVIS_ACTION_BIT;
					}
				}
				else
					result |= BLANK_BIT;
	
				if (the_char == globals->soft_hyphen_char) {
					
					result &= (~(WORD_BREAK_BIT | WORD_SEL_BIT));
					result |= (BLANK_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT | SOFT_HYPHEN_BIT);
				}
				else
					result |=  (WORD_BREAK_BIT | WORD_SEL_BIT);
			}
			else {
				
				if (the_char == globals->fwd_delete_char)
					result |= INVIS_ACTION_BIT;
				else
 				if (font_non_roman && (mask_bits & (WORD_SEL_BIT | WORD_BREAK_BIT))
 					&& (set_non_roman_bits(pg, walker, data, offset_begin,
 					offset_end, char_offset + 1, FIRST_HALF_BIT | LAST_HALF_BIT,
 					NULL, NULL, TRUE) & (FIRST_HALF_BIT | LAST_HALF_BIT)) )
					result |= (WORD_BREAK_BIT | WORD_SEL_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT);
				else
				if (block_offset != NO_BLOCK_OFFSET && last_char_of_script(walker, char_offset + block_offset))
					result |= (WORD_BREAK_BIT | WORD_SEL_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT);
				else
				if ((the_char < '0') && (the_char != global_char_to_int(globals->flat_single_quote)) && (the_char != decimal_char)
						&& (the_char != hyphen_char))
					result |= (WORD_SEL_BIT | INCLUDE_BREAK_BIT);
				else
				if ((the_char == global_char_to_int(globals->left_double_quote))
					|| (the_char == global_char_to_int(globals->right_double_quote)))
					result |= (WORD_SEL_BIT | INCLUDE_BREAK_BIT);
				else {
					
					if (mask_bits & (WORD_BREAK_BIT | INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT | WORD_SEL_BIT)) {
						
						result |= INCLUDE_BREAK_BIT;
						
						if (the_char == hyphen_char)
							result |= WORD_BREAK_BIT;
	
						if (the_char == decimal_char)
							result |= WORD_SEL_BIT;
						else
						if (the_char < 'A' && the_char > '9')
							result |= WORD_SEL_BIT;
						else
						result |= INCLUDE_SEL_BIT;
					}
				}
			}
	
			if (mask_bits & LONG_FORM_INFO) {
				if ((the_char >= 'A') && (the_char <= 'Z'))
					result |= UPPER_CASE_BIT;
				if ((the_char >= '0') && (the_char <= '9'))
					result |= NUMBER_BIT;
				else
				if ((the_char > 0x20) && (the_char < 0x40))
					result |= PUNCT_NORMAL_BIT;
			}
		}
	}  // End Roman char check

	if (symbol_check == decimal_char)
		result |= DECIMAL_CHAR_BIT;
		
	if (mask_bits & QUOTE_BITS) {

		if (global_char_to_int(globals->flat_single_quote) == symbol_check)
			result |= (FLAT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->flat_double_quote) == symbol_check)
			result |= (FLAT_QUOTE_BIT);

		if (global_char_to_int(globals->left_single_quote) == symbol_check)
			result |= (LEFT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->right_single_quote) == symbol_check)
			result |= (RIGHT_QUOTE_BIT | SINGLE_QUOTE_BIT);

		if (global_char_to_int(globals->left_double_quote) == symbol_check)
			result |= (LEFT_QUOTE_BIT);

		if (global_char_to_int(globals->right_double_quote) == symbol_check)
			result |= (RIGHT_QUOTE_BIT);
	}

	if (mask_bits & LOWER_CASE_BIT)
		if (!(result & (QUOTE_BITS | UPPER_CASE_BIT | PUNCT_NORMAL_BIT | NUMBER_BIT | SYMBOL_BIT)))
			result |= LOWER_CASE_BIT;

#ifdef PG_SUBSETS
	if (the_char == SUBREF_CHAR)
		result &= (~BLANK_BIT); 
#endif

	if (mask_bits & WORDBREAK_PROC_BITS)
		if (!(result & (NON_TEXT_BIT | LAST_HALF_BIT | MIDDLE_CHAR_BIT)))
			result = pg->procs.wordbreak_proc(pg, &data[char_offset], char_bytes, walker->cur_style,
					walker->cur_font, result);

	return  result & mask_bits;
}
#endif


/* DEFAULT DRAW TEXT FUNCTION. Obviously machine-dependent. Note that the
font, style, etc. has been pre-set in the current port (or, if your platform
does not do such a thing as pre-set the font and style, use the style info
provided in this function).   Draw_position->to.h & v must get set to
the ending text position, otherwise tab leaders will fail.

NOTE ON TEXT SCALING:  Upon entry, all the begin/ending points for the screen
have ALREADY BEEN SCALED, but the point size of the text is still set to the
non-scaled size.  If scaling is enabled (pg->scale_factor != 0) this function
must take appropriate action to draw in the scaled size.  */

PG_PASCAL (void) pgDrawProc (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
		pg_short_t offset, pg_short_t length, draw_points_ptr draw_position,
		long extra, short draw_mode)
{
	register pg_char_ptr		text;
	style_info_ptr		  		original_style;
	memory_ref			      	transliterate_ref, x_widths_ref, special_locs;
	Point				   		start_pt, end_pen;
	long				    	total_length, draw_length, draw_offset, extra_just, use_extra;
	short				   		use_mode, underline_bits;
	short						small_caps_extra;
	pg_boolean			      	non_transparent_text;
	HDC					    	hdc;
	COLORREF					bk_color;
	int 	PG_FAR				*x_widths;
	int							num_spaces;
	long				   		style_ascent;
	long PG_FAR					*small_caps_locs;
	pg_boolean					printing;

	if (walker->cur_style->class_bits & NON_TEXT_BITS)
		return;
		
	printing = ((pg->flags & PRINT_MODE_BIT) != 0);
	
	original_style = walker->cur_style;
	draw_offset = (long) offset;
	total_length = length;

	use_mode = draw_mode;
	use_extra = extra;
	non_transparent_text = !(pgTransColor(pg->globals, &original_style->bk_color));
	
	if (pg->globals->offscreen_enable == OFFSCREEN_SUCCESSFUL)
		use_mode = direct_or;

	start_pt.h = pgLongToShort(draw_position->from.h);
	start_pt.v = pgLongToShort(draw_position->from.v);
	
	end_pen = start_pt;
	start_pt.h -= (short)(walker->cur_style->right_overhang / 2);

	hdc = pgGetPlatformDevice(pg->globals->current_port);
	walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font, &walker->superimpose, TRUE);

	style_ascent = walker->superimpose.ascent;
	start_pt.v -= (short)(style_ascent + 1);

	if (non_transparent_text) {
	
		pgColorToOS(&original_style->bk_color, (void PG_FAR *)&bk_color);
		bk_color |= pg->port.palette_select;
			
		SetBkColor(hdc, bk_color);
		use_mode = direct_copy;
	}

	underline_bits = SpecialUnderline(&walker->superimpose);

	if (original_style->styles[small_caps_var] && extra) {

		special_locs = pgGetSpecialLocs(pg, draw_position->block, draw_position->starts,
				length, extra, PLAIN_SCALE);

		small_caps_locs = UseMemory(special_locs);
	}
	else
		small_caps_locs = NULL;

	while (total_length) {
		
		draw_length = total_length;
		small_caps_extra = 0;
		
		if (original_style->styles[small_caps_var]) {
			long	    info_flags;
			
			draw_length = pgSameCaseLength(pg, walker, &data[draw_offset],
				draw_position->real_offset, total_length, &info_flags);

			if (!info_flags)
				walker->cur_style = UseMemoryRecord(pg->t_formats,
						original_style->small_caps_index, 0, FALSE);

			small_caps_extra = (short)(original_style->ascent - walker->cur_style->ascent);
			
			walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font, &walker->superimpose, TRUE);
		}

		text = data + draw_offset;
		if (transliterate_ref = pgConvertTextCaps(pg, original_style, text, draw_length))
			text = UseMemory(transliterate_ref);

		if (small_caps_locs) {
			long			compensated_width, real_width;
			
			compensated_width = small_caps_locs[draw_length] - *small_caps_locs;

			pgSetMeasureDevice(pg);		
			walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font, &walker->superimpose, FALSE);
			real_width = GetTextWidth((HDC)pg->port.machine_ref, (pg_char_ptr)text, (int)draw_length);
			pgUnsetMeasureDevice(pg);

			use_extra = compensated_width - real_width;
			small_caps_locs += draw_length;
		}

		extra_just = use_extra;

		if (use_mode == direct_copy)
			SetBkMode(hdc, OPAQUE);
		else
		    SetBkMode(hdc, TRANSPARENT);
        
        SetTextAlign(hdc, TA_LEFT);      // MM found this one

	    if (extra_just && draw_length)
	    	num_spaces = get_space_extra(hdc, text, (short)draw_length, 0);
		else
			num_spaces = 0;

		if (printing)
			start_pt.v += extra_print_descent(pg, hdc, walker->cur_style);
		
	    if ((walker->cur_style->char_extra || (walker->cur_style->maintenance & HAS_FORCED_WIDTHS)
	    		|| (extra_just && num_spaces == 0) || printing || pg->port.print_port || pg->port.scale.scale)
	    		&& (draw_length > 1)) {
	    	rectangle		bounds;
	    	RECT			bk_box;
	    	UINT			flags;
              
	    	if (use_mode == direct_copy)
	    		flags = ETO_OPAQUE;
	    	else
	    		flags = 0;
	    	
	    	bounds = draw_position->starts->bounds;
	    	bk_box.left = start_pt.h;
	    	bk_box.top = (short)(draw_position->from.v - draw_position->ascent);
	    	bk_box.bottom = bk_box.top + (short)(bounds.bot_right.v - bounds.top_left.v);
	    	bk_box.right = bk_box.left + (short)(bounds.bot_right.h - bounds.top_left.h);
	    		
	    	x_widths_ref = pg->globals->alpha_widths;
	    	
	    	SetMemorySize(x_widths_ref, draw_length + 1);
	    	x_widths = UseMemory(x_widths_ref);
	    	
			pgSetMeasureDevice(pg);
		
			walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font, &walker->superimpose, FALSE);
    		end_pen.h += (short)(pgMeasureText(pg, measure_textout, text,
    			draw_length, use_extra, (long)num_spaces, x_widths, walker));
  
			pgUnsetMeasureDevice(pg);

	    	ExtTextOut(hdc, start_pt.h, start_pt.v + small_caps_extra, flags, &bk_box, (PGSTR)text,
	    			(UINT)draw_length, x_widths);
	    	UnuseMemory(x_widths_ref);
	    }
	    else {

	    	if (extra_just && draw_length)
				if (num_spaces)
					SetTextJustification(hdc, (short)use_extra, num_spaces);

			TextOut(hdc, start_pt.h, start_pt.v + small_caps_extra, (PGSTR) text, (int)draw_length);
	    	end_pen.h += (short)GetTextWidth(hdc, (pg_char_ptr)text, (int)draw_length);
	    }

		if (transliterate_ref)
			UnuseMemory(transliterate_ref);
	
		if (draw_position->starts->flags & WORD_HYPHEN_BIT) {
		    short		hyphen_size;
		    PGSTR		p;
		    
		    hyphen_size = global_char_size(pg->globals->hyphen_char);
#ifndef GLOBALS_ARE_PSTRINGS
			p = (PGSTR)&pg->globals->hyphen_char[0];
			TextOut(hdc, end_pen.h, start_pt.v + small_caps_extra, p, hyphen_size);   
			end_pen.h += (short)GetTextWidth(hdc, p, hyphen_size);
#else
			p = (PGSTR)&pg->globals->hyphen_char[1];
			TextOut(hdc, end_pen.h, start_pt.v + small_caps_extra, p, hyphen_size);   
			end_pen.h += (short)GetTextWidth(hdc, p, hyphen_size);
#endif
	    }

		draw_position->to.h = end_pen.h;
		draw_position->to.v = end_pen.v;
		
		total_length -= draw_length;
		draw_offset += draw_length;
		start_pt.h = end_pen.h;
		walker->cur_style = original_style;
	}

	if (original_style->styles[small_caps_var])
		original_style->procs.install(pg, original_style, walker->cur_font, &walker->superimpose, FALSE);
		
	if (walker->superimpose.styles[boxed_var])
		draw_box_style(pg, walker, draw_position);

	if (small_caps_locs)
		UnuseAndDispose(special_locs);

	start_pt.h = pgLongToShort(draw_position->from.h);
	start_pt.v = pgLongToShort(draw_position->from.v);

	if (underline_bits) {
		
		walker->current_offset = draw_position->real_offset;

		if (underline_bits & WORD_UNDERLINE)
			pgDrawWordUnderline(pg, walker, underline_bits, data, offset, length, extra, draw_position);
		else
			pgDrawSpecialUnderline(pg, start_pt, (short)(end_pen.h - start_pt.h),
					walker->cur_style, underline_bits);

		pgSetWalkStyle(walker, draw_position->real_offset);
	}

	if (walker->cur_style->styles[hidden_text_var]) {
		HPEN		 hidden_pen, old_pen;
		TEXTMETRIC	 tm;
		short		 vertical;
		
		GetTextMetrics(hdc, &tm);
		
		hidden_pen = CreatePen(PS_SOLID, 1, (COLORREF)0x00808080);
		old_pen = SelectObject(hdc, hidden_pen);
		vertical = (short)(start_pt.v - (tm.tmAscent / 3));
		MoveToEx(hdc, start_pt.h, vertical, NULL);
		LineTo(hdc, end_pen.h, vertical);
		SelectObject(hdc, old_pen);
		DeleteObject(hidden_pen);
	}

	if (non_transparent_text) {
	
		pgColorToOS(&pg->bk_color, (void PG_FAR *)&bk_color);
		bk_color |= pg->port.palette_select;;
		SetBkColor(hdc, bk_color);
	}

	pgReleasePlatformDevice(pg->globals->current_port);
} 


/* DEFAULT TAB DRAW PROC (for leaders, etc.). Obviously machine-dependent. */

PG_PASCAL (void) pgTabDrawProc (paige_rec_ptr pg, style_walk_ptr walker, tab_stop_ptr tab,
		draw_points_ptr draw_position)
{
	memory_ref	      	filler;
	pg_char_ptr	     	filler_text;
	pg_char		    	check_filler[6];
	pg_short_t			leader;
	short		   		single_width, needed_width, filler_size, filler_width;
	short		   		underline_bits, i;
    
	if ((leader = (pg_short_t)(tab->leader & 0x0000FFFF)) != 0) {

		for (i = 0; i < 5; ++i)
			check_filler[i] = (pg_char)leader;
	
		single_width = GetTextWidth((HDC)pg->port.machine_ref, (PGSTR)check_filler, 4) / 4;

		needed_width = (short)(draw_position->to.h - draw_position->from.h);
	
		filler_size = needed_width / single_width + 2;
		filler = MemoryAlloc(pg->globals->mem_globals, sizeof(pg_char),
				filler_size, 0);
		filler_text = UseMemory(filler);
		pgFillBlock(filler_text, filler_size * sizeof(pg_char), (char)leader);
	
		while (filler_size) {
			
			filler_width = GetTextWidth((HDC)pg->port.machine_ref, (PGSTR)filler_text, filler_size);
			if (filler_width <= needed_width)
				break;
			
			--filler_size;
		}
		
		if (filler_size) {
			HDC		     hdc;

			hdc = pgGetPlatformDevice(pg->globals->current_port);
			TextOut(hdc, (short) draw_position->to.h - filler_width,
				(short) (draw_position->to.v - walker->superimpose.ascent),
					(PGSTR) filler_text, filler_size);
			pgReleasePlatformDevice(pg->globals->current_port);
		}
	
		UnuseMemory(filler);
		DisposeMemory(filler);
	}

	underline_bits = SpecialUnderline(walker->cur_style);

	if (walker->cur_style->styles[underline_var])
		underline_bits |= FAKE_UNDERLINE;
	
	if (underline_bits) {
		Point	   start_pt;
		
		start_pt.h = pgLongToShort(draw_position->from.h);
		start_pt.v = pgLongToShort(draw_position->from.v + 1);

		pgDrawSpecialUnderline(pg, start_pt, (short) (draw_position->to.h - draw_position->from.h),
				walker->cur_style, underline_bits);
	}
}


/* pgSpecialCharProc is the default "draw special character" function. This gets
called by Paige when a control character is encountered in drawing mode. The
purpose of this function is to show "invisibles."  It won't get called unless
"show invisibles" is active within the paige item.  */

PG_PASCAL (void) pgSpecialCharProc (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
		pg_short_t offset, pg_short_t length, draw_points_ptr draw_position,
		long extra, short draw_mode)
{
	register pg_globals_ptr		 	globals;
	register pg_short_t			    flags;
	point_start_ptr					starts;
	co_ordinate					    draw_point, tab_point;

	globals = pg->globals;
	starts = draw_position->starts;
	flags = starts->flags;
	draw_point = draw_position->from;
	tab_point = draw_position->to;
	draw_point.h += (starts->bounds.bot_right.h - starts->bounds.top_left.h);
	
	draw_point.v -= set_invisible_symbol_style(pg);
    tab_point.v = draw_point.v;
    
	if (flags & PAR_BREAK_BIT)
		draw_special_char(pg, globals->cr_invis_symbol, &draw_point);
	if (flags & SOFT_PAR_BIT)
		draw_special_char(pg, globals->lf_invis_symbol, &draw_point);
	if (flags & TAB_BREAK_BIT)
		draw_special_char(pg, globals->tab_invis_symbol, &tab_point);
	if (flags & BREAK_CONTAINER_BIT)
		draw_special_char(pg, globals->cont_invis_symbol, &draw_point);
	if ((draw_position->real_offset + length) == pg->t_length)
		if (!(flags & (PAR_BREAK_BIT | SOFT_PAR_BIT)))		
			draw_special_char(pg, globals->end_invis_symbol, &draw_point);

	if (global_char_size(globals->space_invis_symbol) && length) {
		register long PG_FAR *locs;
		register pg_short_t  local_offset;
		memory_ref		     special_locs;
		
		special_locs = pgGetSpecialLocs(pg, draw_position->block, draw_position->starts,
				length, extra, PLAIN_SCALE);

		locs = UseMemory(special_locs);

		local_offset = 0;
		draw_point.h = draw_position->from.h;
		
		while (local_offset < length) {

			if (walker->cur_style->procs.char_info(pg, walker, data, draw_position->block->begin,
				offset, length, local_offset + offset, BLANK_BIT | CTL_BIT) == BLANK_BIT) {

				draw_point.h = (short) (draw_position->from.h + *locs);
				draw_special_char(pg, globals->space_invis_symbol, &draw_point);
			}

			++local_offset;
			++locs;
		}
		
		UnuseAndDispose(special_locs);
	}
	
	walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font,
			&walker->superimpose, (short)(globals->offscreen_port.machine_ref != 0));
}



/* DEFAULT FONT INIT PROC. This is machine-dependent. */

PG_PASCAL (void) pgInitFont (paige_rec_ptr pg, font_info_ptr info)
{
	HDC			   hdc;
	short		   name_length;

	if (!info->name[0])
		return;

	if (!info->platform)
		info->platform = PAIGE_GRAPHICS;

	info->environs &= (~FONT_USES_ALTERNATE);

	pgFixFontName(info);
	//¥ TRS/OITC
	if ((name_length = FONT_NAME_SIZE(info->name) + 1) > (FONT_SIZE)) {
	
		name_length = FONT_SIZE - 1;
		info->name[0] = (pg_char)name_length;
	}
	
	//¥ TRS/OITC
	pgFillBlock(&info->name[name_length], (FONT_SIZE - name_length) * sizeof(pg_char), 0);

	info->environs &= (~(FONT_NOT_AVAIL | FONT_BEST_GUESS));

/* Validate the font record per platform: */
		
	if ( (info->environs & FONT_GOOD) || (pgIsRealFont(pg->globals, info, FALSE)))
		info->environs |= FONT_GOOD;
	else {
		
		if (pgIsRealFont(pg->globals, info, TRUE))
			info->environs |= FONT_GOOD | FONT_USES_ALTERNATE;
		else
			info->environs |= (FONT_NOT_AVAIL | FONT_BEST_GUESS);
	}

    if (!info->machine_var[PG_OUT_PRECISION])
    	info->machine_var[PG_OUT_PRECISION] = OUT_DEFAULT_PRECIS;
    if (!info->machine_var[PG_CLIP_PRECISION])
    	info->machine_var[PG_CLIP_PRECISION] = CLIP_DEFAULT_PRECIS;
    if (!info->machine_var[PG_QUALITY])
    	info->machine_var[PG_QUALITY] = DRAFT_QUALITY;
	
	if (name_length == 1)  { /* null font name = default for this window */
		
		hdc = pgGetPlatformDevice(&pg->port);
		GetTextFace(hdc, FONT_SIZE, &info->name[1]);
		info->name[0] = (pg_char)pgCStrLength((pg_c_string_ptr)&info->name[1]);

		pgReleasePlatformDevice(&pg->port);
	}

	init_font_record (pg, info);
}




/* pgInitDefaultFont must initialize a font record to the default settings.
NOTE: Upon entry, the entire record has been filled with zeros. Note that
for Macintosh, only the font name is initialized. This is because all other
fields get set when the general hook, "pgInitFont" gets called. */

PG_PASCAL (void) pgInitDefaultFont (const pg_globals_ptr globals, font_info_ptr font)
{
	HDC				hdc;
	short			char_type;

	font->environs = FONT_GOOD;
	font->platform = PAIGE_GRAPHICS;
    pgIsRealFont(globals, font, FALSE);
 
    if (!font->machine_var[PG_OUT_PRECISION])
    	font->machine_var[PG_OUT_PRECISION] = OUT_DEFAULT_PRECIS;
    if (!font->machine_var[PG_CLIP_PRECISION])
    	font->machine_var[PG_CLIP_PRECISION] = CLIP_DEFAULT_PRECIS;
    if (!font->machine_var[PG_QUALITY])
    	font->machine_var[PG_QUALITY] = DRAFT_QUALITY;
	if (!font->machine_var[PG_CHARSET])
#ifndef WIN_CHARSET
		font->machine_var[PG_CHARSET] = DEFAULT_CHARSET;
#else
		font->machine_var[PG_CHARSET] = WIN_CHARSET;
#endif
	if (font->name[0] == 0)  { /* null font name = default for this window */

		hdc = (HDC)globals->machine_const;
		GetTextFace(hdc, FONT_SIZE - 1, (PGSTR)&font->name[1]);
		font->name[0] = lstrlen(&font->name[1]);
	}
#ifdef WIN_MULTILINGUAL	

#ifdef WIN16_COMPILE
		font->code_page = GetKBCodePage();
		font->char_type = (short)font->machine_var[PG_CHARSET];
#else
		font->code_page = GetConsoleOutputCP();
		font->char_type = GetTextCharset((HDC)globals->machine_const) & SCRIPT_CODE_MASK;
#endif
		font->environs |= FONT_BEST_GUESS;
		init_font_language(font, (HDC)globals->machine_const);
#else
	font->char_type = WIN_CHARSET;		//Win16 character set
	font->code_page = WIN_CODEPAGE;		//Win16 code page
	font->language = WIN_LANGUAGE;		//Win16 language
#endif
	char_type = font->char_type & SCRIPT_CODE_MASK;
	
	if (font->machine_var[PG_CHARSET] > SYMBOL_CHARSET)
		font->char_type |= NON_ROMAN_FLAG;

	if (char_type == HEBREW_CHARSET || char_type == ARABIC_CHARSET || char_type == THAI_CHARSET)
		font->char_type |= RIGHT_FONT_FLAG;
}



/* pgSetGrafDevice should "install" the graphics device to become the target
for subsequent display (if verb == set_pg_device). The function should also preserve the state of the
device (such as save off the current device and current settings) as necessary.
If bk_color is not NULL the background color gets set to that color.
Note: This function will always get balanced with a subsequent call with unset_pg_device. */

PG_PASCAL (void) pgSetGrafDevice (paige_rec_ptr pg, short verb, graf_device_ptr device,
			color_value_ptr bk_color)
{
	register port_preserve	  	PG_FAR *preserve_ptr;
	pg_globals_ptr 				globals;
	HDC			     			hdc;
	COLORREF					color;
	long					    stack_size;

	globals = pg->globals;

	if (verb == unset_pg_device) {
	
		stack_size = GetMemorySize(device->graf_stack) - 1;
		preserve_ptr = UseMemory(device->graf_stack);
		preserve_ptr += stack_size;

		globals->current_port = preserve_ptr->last_device;
	
		if (preserve_ptr->saved_dc_index)
			RestoreDC((HDC)device->machine_ref, preserve_ptr->saved_dc_index);
		
		pgReleasePlatformDevice(device);
		UnuseMemory(device->graf_stack);
		SetMemorySize(device->graf_stack, stack_size);
	}
	else {
	
		preserve_ptr = AppendMemory(device->graf_stack, 1, FALSE);
		preserve_ptr->last_device = globals->current_port;
		globals->current_port = device;
	
		hdc = pgGetPlatformDevice(device);
		GetClipBox(hdc, (LPRECT)&preserve_ptr->rgn_box);
		preserve_ptr->saved_dc_index = SaveDC(hdc);

		if (pg->doc_info.attributes & COLOR_VIS_BIT)
			device->bk_color = pg->bk_color;
		else
		if (bk_color) {
		
			if ((!pgEqualColor(bk_color, &device->bk_color)))
			    device->bk_color = *bk_color;
		}
	
		pgColorToOS(&device->bk_color, (void PG_FAR *)&color);
	    color |= device->palette_select;;
		SetBkColor(hdc, color);
		UnuseMemory(device->graf_stack);
	}
}



/* pgPrepareOffscreen should set up an offscreen drawing port to be later
transfered to the screen. The (ultimate) target area is target_area. If the
offscreen device is set up, pass the h,v amounts to adjust drawing in offset_adjust.
The actual offscreen transfer occurs in pgFinishOffscreen. For Macintosh, the
offscreen bitMap port is set as the current port when this function exits.

IF YOU CANNOT DO THIS ON YOUR PLATFORM, you should do nothing but erase the
target_area rectangle and pass 0,0 for offset_adjust.

Change 3/29/94 -- real_bits_target specifies the actual area that needs bitmap
transfer. If different than target_area then smaller bitmaps can be created
and the remainder "erased" for increased performance and less memory use. For
example, a portion of the line might be graphics that is drawn separately.

Change 10/16/94 -- we no longer do direct drawing to screen if bitmap too
large. Instead, we make smaller rect and real_bits_target gets set to the
actual target area we have used. So if bits too large for one pass, the caller
must make multiple passes until the bitmap drawing is complete. */

PG_PASCAL (void) pgPrepareOffscreen (paige_rec_ptr pg, rectangle_ptr target_area,
			rectangle_ptr real_bits_target, co_ordinate_ptr offset_adjust,
			long text_offset, point_start_ptr line_start, short draw_mode)
{
	register pg_globals_ptr globals;
    rectangle			    bits_rect;
	Rect				    target;
	HDC		    			bitmap_dc;
	HBITMAP	 				bitmap;
	COLORREF				bk_color;
	long					data_size, container_num;
	short					pix_size, row_bytes;

	globals = pg->globals;

	*real_bits_target = *target_area;
    container_num = 0;
    
    if (pg->port.scale.scale) {

 		offset_adjust->h = offset_adjust->v = 0;
		globals->offscreen_enable = OFFSCREEN_UNSUCCESSFUL;
   
		if (draw_mode == bits_copy) {
			HRGN			temp_clip;
            RECT			clip_rect;
            rectangle		scaled_rect;
            co_ordinate		scroll_adjust;
            int				dc_save;
            
			pg->procs.container_proc(pg, (pg_short_t)container_num, real_bits_target, NULL,
				(co_ordinate_ptr)NULL, erase_rect_verb, (void PG_FAR *)NULL);
			
			scroll_adjust = pg->scroll_pos;
			pgNegatePt(&scroll_adjust);
			pgAddPt(&pg->port.origin, &scroll_adjust);
			pgScaleRectToRect(&pg->port.scale, real_bits_target, &scaled_rect, &scroll_adjust);
           	RectangleToRect(&scaled_rect, NULL, &clip_rect);

			temp_clip = CreateRectRgn(clip_rect.left,
						clip_rect.top,
						clip_rect.right + 1,
						clip_rect.bottom + 1);
			dc_save = SaveDC((HDC)pg->port.machine_ref);
			SelectClipRgn((HDC)pg->port.machine_ref, temp_clip);
			pg->procs.bitmap_proc(pg, &pg->port, FALSE,
				real_bits_target, &scroll_adjust, text_offset);
			DeleteObject(temp_clip);
			RestoreDC((HDC)pg->port.machine_ref, dc_save);
		}

		return;       	
    }
 
 	if (line_start)
		container_num = line_start->r_num - 1;
	else
		container_num = 0;

	offset_adjust->h = -real_bits_target->top_left.h;
	offset_adjust->v = -real_bits_target->top_left.v;       
    globals->offscreen_port.access_ctr += 1;
    
	if (globals->offscreen_port.access_ctr == 1) {

	    globals->original_target = globals->offscreen_target = *target_area;
		RectangleToRect(target_area, offset_adjust, &target);
	
		bitmap_dc = CreateCompatibleDC((HDC)pg->port.machine_ref);
		
		if (pg->port.palette) {
			
			SelectPalette(bitmap_dc, (HPALETTE)pg->port.palette, FALSE);
			RealizePalette(bitmap_dc);
			globals->offscreen_port.palette_select = 0x02000000L;
		}
	    else
	    	globals->offscreen_port.palette_select = 0;

		pix_size = GetDeviceCaps(bitmap_dc, BITSPIXEL);
		row_bytes = ((target.right / 8) + 1) * pix_size;
		if (row_bytes & 1)
			++row_bytes;
		
		data_size = (long)(row_bytes * target.bottom);
		
		if (data_size > globals->max_offscreen) {
			
			target.bottom = (short) (globals->max_offscreen / row_bytes);
	
			globals->offscreen_target.bot_right.v
						= globals->offscreen_target.top_left.v + (long)target.bottom;
			*real_bits_target = globals->offscreen_target;
		}
	
		bitmap = CreateCompatibleBitmap((HDC)pg->port.machine_ref, target.right, target.bottom);

		globals->offscreen_buf = PG_LONGWORD(memory_ref) bitmap;
		globals->offscreen_port.previous_items[PREVIOUS_BITMAP] = PG_LONGWORD(long)SelectObject(bitmap_dc, bitmap);
		globals->offscreen_port.machine_ref = PG_LONGWORD(generic_var) bitmap_dc;
        globals->current_port = (graf_device_ptr) &globals->offscreen_port;
		pgColorToOS(&pg->bk_color, &bk_color);
		bk_color |= pg->port.palette_select;
		SetBkColor(bitmap_dc, bk_color);
		    
		if (!pgEqualColor(&pg->bk_color, &globals->offscreen_port.bk_color))
		    globals->offscreen_port.bk_color = pg->bk_color;
	    	
	    globals->offscreen_enable = OFFSCREEN_SUCCESSFUL;
	  
	  	pgSetShapeRect(globals->offscreen_exclusion, NULL);
		RectToRectangle(&target, &bits_rect);
		pgEraseRect(globals, &bits_rect, NULL, NULL);
	}
	
	if (globals->offscreen_enable == OFFSCREEN_SUCCESSFUL) {

		globals->current_port = (graf_device_ptr) &globals->offscreen_port;

		pg->procs.bitmap_proc(pg, &globals->offscreen_port, FALSE,
			&globals->offscreen_target, offset_adjust, text_offset);
	}
}


/* pgFinishOffscreen gets called after pgPrepareOffscreen set up an offscreen
port and text was drawin to it. The purpose of pgFinishOffscreen is to transfer
the "bits" from the offscreen port to the graphics device that was set before
establishing the offscreen device. IF YOU CANNOT DO THIS ON YOUR PLATFORM, this
function will do nothing.

Added 10-17-94, the function returns a Boolean value if the transfer did not
fill the entire area, and if so, the bitmap is kept "alive," the target frame
is offset and new_offset is adjusted to the new drawing offset. In other words,
another pass must be made drawing the same thing but at offset_adjust. ). */

PG_PASCAL (pg_boolean) pgFinishOffscreen (paige_rec_ptr pg, long text_offset,
		point_start_ptr line_start, co_ordinate_ptr new_offset,
		rectangle_ptr new_target, short draw_mode)
{
	register pg_globals_ptr 	globals;
	rectangle			        bits_rect, next_target;
	co_ordinate			     	offset_adjust;
	Rect				    	target, source;

	globals = pg->globals;

	if (pg->port.scale.scale || globals->offscreen_enable != OFFSCREEN_SUCCESSFUL) {
		
		globals->offscreen_enable = FALSE;
		
		return	TRUE;
	}

	offset_adjust.h = -globals->offscreen_target.top_left.h;
	offset_adjust.v = -globals->offscreen_target.top_left.v;
	bits_rect = globals->offscreen_target;
	pgOffsetRect(&bits_rect, offset_adjust.h, offset_adjust.v);
	pg->procs.bitmap_proc(pg, &globals->offscreen_port, TRUE,
		&globals->offscreen_target, &offset_adjust, text_offset);
	
	if ((--globals->offscreen_port.access_ctr) == 0) {

		RectangleToRect(&globals->offscreen_target, NULL, &target);
		source = target;		
		source.right -= source.left;
		source.bottom -= source.top;
		source.top = source.left = 0;

		BitBlt((HDC)pg->port.machine_ref, target.left, target.top,
				source.right, source.bottom, (HDC)globals->offscreen_port.machine_ref,
				0, 0, SRCCOPY);
		
		SelectObject((HDC)globals->offscreen_port.machine_ref, (HANDLE)globals->offscreen_port.previous_items[PREVIOUS_BITMAP]);
		DeleteDC((HDC)globals->offscreen_port.machine_ref);
		DeleteObject((HBITMAP)globals->offscreen_buf);
		globals->offscreen_port.machine_ref = MEM_NULL;
		
		globals->current_port = (graf_device_ptr)&pg->port;
	
		globals->offscreen_enable = FALSE;
	
		if (globals->offscreen_target.bot_right.v < globals->original_target.bot_right.v) {
			
			next_target = globals->original_target;
			next_target.top_left.v = globals->offscreen_target.bot_right.v;
	
			pgPrepareOffscreen(pg, &next_target, new_target, new_offset, text_offset,
					line_start, draw_mode);
	
			return	FALSE;
		}
	}

	return	TRUE;
}


/* pgScrollRect must scroll a rectangle's contents h or v pixels.
NOTE: Both distance_h and distance_v will never be non-zero, only one or the
other will contain a non-zero value. Positive values must move the contents
down or right, negative values up or left.
ADDITION 1/14/94: "target_device" had been added due to background color potential
problem. Mainly, if background color temporarily set to something else the
scrolling function, at least for Mac, needs to restore the color to the original
background so it "fills in" correctly.  Note, however, that all the correct
statuses are currently "pushed" and target_device is the current port.
The affected area gets added to affected_area shape.

Change for 2.0 -- this function returns a region (pg_region) in addition to the shape.
If there is already a scroll_rgn in pg->port, the otherwise function result is
combined with it and that region is returned. */

PG_PASCAL (pg_region) pgScrollRect (paige_rec_ptr pg, rectangle_ptr rect,
		long distance_h, long distance_v, rectangle_ptr affected_area, short draw_mode)
{
	pg_region		    scroll_rgn;
	Rect			    scroll_rect, update_rect;
	HDC		     		hdc;
	short	   			amount_h, amount_v;
    
    RectangleToRect(rect, NULL, &scroll_rect);

	scroll_rgn = pgCreateRgn();

	amount_h = pgLongToShort(distance_h);
	amount_v = pgLongToShort(distance_v);
		
	hdc = pgGetPlatformDevice(&pg->port);
	
	if (pg->port.scale.scale)
		SetMapMode(hdc, MM_TEXT);

	ScrollDC(hdc, amount_h, amount_v,
			(RECT far *)&scroll_rect, (RECT far *) &scroll_rect, scroll_rgn, (RECT far *)&update_rect);
	
	if (affected_area) {
	    
	    GetRgnBox(scroll_rgn, &update_rect);	
	    RectToRectangle((RECT PG_FAR *)&update_rect, affected_area);
 	}
 
 	if (pg->doc_info.attributes & COLOR_VIS_BIT) {
 		HBRUSH			bk_brush;
 		
 		bk_brush = build_background_brush(&pg->port);
	    FillRgn(hdc, scroll_rgn, bk_brush);
	    DeleteObject(bk_brush);
    }
    else
   	 	PaintRgn(hdc, scroll_rgn);

    if (pg->port.scale.scale)
    	scale_dc(hdc, &pg->port);

	pgReleasePlatformDevice(&pg->port);

	if (pg->port.scroll_rgn) {
		
		pgUnionRgn(scroll_rgn, pg->port.scroll_rgn, pg->port.scroll_rgn);
		pgDisposeRgn(scroll_rgn);
		scroll_rgn = pg->port.scroll_rgn;
	}

	return		scroll_rgn;
}


/* pgScaleGrafDevice should prepare whatever it has to for subsequent device scaling. For
Windows we zero-out the main scale factor in pg and place it in the device scaling field. */

PG_PASCAL (void) pgScaleGrafDevice (paige_rec_ptr pg)
{
	pg->port.scale = pg->scale_factor;
	pg->port.scale.scale = pg->real_scaling;
	
	pg->scale_factor.scale = 0;
}



/* REVISION 2.0 --
pgClipGrafDevice sets the clipping region for pg->port. The clip_verb controls how the
window is clipped, as follows:  clip_standard_verb clips to the vis_area minus the exclusion
area (and intersected with page_area if appropriate pg attributes are set);
clip_with_current_verb is the same as clip_standard_verb except it is also intersected with
the current clipping region; clip_with_none_verb sets vis_area only (no exclusions are
subtracted and page_area not intersected).
For all clip_verb values, if alternate_vis is non-NULL then it is used instead of the pg's vis
area for computing the regions. */

PG_PASCAL (void) pgClipGrafDevice (paige_rec_ptr pg, short clip_verb, shape_ref alternate_vis)
{
	HDC					hdc;
	pg_scale_factor		scaler;
	rectangle			vis_bounds;
	pg_region			intersect_region, clip_region;
	shape_ref			use_vis;
	RECT				vis_r;
	pg_boolean			scale_vis_area;

	if ((pg->flags & PRINT_MODE_BIT) || (pg->doc_info.attributes & NO_CLIP_REGIONS))
		return;
	
	clip_region = pg->port.clip_rgn;
	hdc = pgGetPlatformDevice(&pg->port);

	if (!(use_vis = alternate_vis))
		use_vis = pg->vis_area;

	#ifdef PG_DEBUG
	if (!hdc)
		pg->globals->mem_globals->debug_proc(PROGRAM_BUG_ASSERT, pg->port.graf_stack);
	#endif

	scale_vis_area = (pg_boolean)((pg->flags & SCALE_VIS_BIT) != 0);
	pgShapeBounds(use_vis, &vis_bounds);

	scaler = pg->port.scale;

	if (scale_vis_area) 
		pgScaleRect(&scaler, NULL, &vis_bounds);

	RectangleToRect(&vis_bounds, NULL, &vis_r);

	switch (clip_verb) {

		case clip_standard_verb:
			
			if (pgVisRegionChanged(pg, TRUE)) {
				
				pgRectToRgn(clip_region, &vis_r);

				if (!(pg->doc_info.attributes & NO_CLIP_PAGE_AREA)
					&& (pg->doc_info.attributes & BOTTOM_FIXED_BIT)) {
					// Compute the page_area region
					
					intersect_region = pgCreateRgn();
					pgBuildPageRegion(pg, &scaler, intersect_region);
					pgSectRgn(clip_region, intersect_region, clip_region);
					pgDisposeRgn(intersect_region);
				}
				
				if (!pgEmptyShape(pg->exclude_area)) {
					
					intersect_region = pgCreateRgn();
	
					if (pgBuildExclusionRegion(pg, &scaler, intersect_region))
						pgSubRgn(clip_region, intersect_region, clip_region);
	
					pgDisposeRgn(intersect_region);
				}
			}

			break;
			
		case clip_with_none_verb:
			clip_region = pgCreateRgn();
			pgRectToRgn(clip_region, &vis_r);
			break;
	}

	if (pg->port.scroll_rgn) {
		
		intersect_region = pgCreateRgn();
		pgSectRgn(clip_region, pg->port.scroll_rgn, intersect_region);
		SelectClipRgn(hdc, intersect_region);
		pgDisposeRgn(intersect_region);
	}
	else
		SelectClipRgn(hdc, clip_region);

	if (clip_verb == clip_with_none_verb)
		pgDisposeRgn(clip_region);
	
	pgReleasePlatformDevice(&pg->port);
}


/* Added 4/12/94, pgSetMeasureDevice is called just before measuring character
widths. Its purpose is to QUICKLY set up some kind of screen device, if
necessary, so the main window isn't affected by subsequent font/style settings.
This may or may not apply to the machine, but for Macintosh we (a) save off
the current port, and (b) set the offscreen port. THIS IS INTENDED TO BE FAST.
(We used to call pgSetGrafDevice which is too slow).  */

PG_PASCAL (void) pgSetMeasureDevice (paige_rec_ptr pg)
{
	pg_globals_ptr			globals;
	
	globals = pg->globals;
	
	if (pg->port.print_port) {
		HDC				printer_dc;
		
		pg->port.machine_ref4 = pg->port.machine_ref;
		pg->port.machine_ref = pg->port.print_port;
		++pg->port.access_ctr;
		printer_dc = (HDC)pg->port.print_port;
		set_printer_scaling(pg, printer_dc, 0, 0);
	}

	pg->procs.set_device(pg, set_pg_device, &pg->port, NULL);
}


/* Added 4/12/94, pgUnsetMeasureDevice unsets whatever was set up in
pgSetMeasureDevice. For Macintosh we restore the previous port. */

PG_PASCAL (void) pgUnsetMeasureDevice (paige_rec_ptr pg)
{
	pg->procs.set_device(pg, unset_pg_device, &pg->port, NULL);

	if (pg->port.print_port) {
		
		pg->port.machine_ref = pg->port.machine_ref4;
		pg->port.machine_ref4 = 0;
		--pg->port.access_ctr;
		
		unset_printer_scaling((HDC)pg->port.print_port);
	}
}



/* pgEraseRect must fill the rectangle "rect" with the current background
color (or whatever is standard with machine). The globals are given
in case the platform needs it to ascertain the "current port." If scaling
is non-NULL the rect should be scaled by that much. If offset_extra is non-NULL
the rect should be offset by h & v amounts. */

PG_PASCAL (void) pgEraseRect (pg_globals_ptr globals, rectangle_ptr rect,
		pg_scale_ptr scaling, co_ordinate_ptr offset_extra)
{
	rectangle		   	converted_r;
	HBRUSH				bk_brush;
	HDC					hdc;
	Rect			    r;
	
    if (pgEmptyRect(rect))
		return;
    
	pgScaleRectToRect(scaling, rect, &converted_r, offset_extra);
	RectangleToRect(&converted_r, NULL, &r);
	hdc = pgGetPlatformDevice(globals->current_port);
	bk_brush = build_background_brush(globals->current_port);
	FillRect(hdc, (LPRECT)&r, bk_brush);
	DeleteObject(bk_brush);
	pgReleasePlatformDevice(globals->current_port);
}


/* pgScalePointSize must scale the text to the appropriate point size based on
the scaling factor in pg, if any. The point_size param holds the current point size.
Text and length hold the text that will be drawn. The function result is
the amount of extra pixels that need to be compensated (plus or minus) to make
up for any scaling errors.
Modified June 26 94, point size scaled is CURRENT font and walker param
is machine-specific (ignored for Mac but used for Windows). */

PG_PASCAL (short) pgScalePointSize (paige_rec_ptr pg, style_walk_ptr walker,
		pg_char_ptr text, long length, pg_boolean PG_FAR *did_scale)
{
	HDC					hdc;
	long				pixeldiff;
	long		    	scaled_resolution, actual_extent, should_be_extent;
    
    *did_scale = FALSE;
    pixeldiff = 0;

	if (pg->scale_factor.scale && length) {
		
		hdc = pgGetPlatformDevice(&pg->port);
		
		*did_scale = TRUE;
		scaled_resolution = pg->resolution >> 16;		
		set_alternate_pointsize(pg, scaled_resolution, walker);
        should_be_extent = GetTextWidth(hdc, (PGSTR)text, (int)length);
		should_be_extent &= 0x0000FFFF;
		pgScaleLong(pg->scale_factor.scale, 0, &should_be_extent);
		
		pgScaleLong(pg->scale_factor.scale, 0, &scaled_resolution);
		set_alternate_pointsize(pg, scaled_resolution, walker);

        actual_extent = (long)GetTextWidth(hdc, (PGSTR)text, (int)length);
        actual_extent &= 0x0000FFFF;

		pgReleasePlatformDevice(&pg->port);
		
		pixeldiff = should_be_extent - actual_extent;
	}

	return  (short)pixeldiff;
}



/* pgIsRealFont returns TRUE if the font name exists in the system. If substitute
is non-NULL it is initialized to a name that does exist (used only for Windows).
*/

PG_PASCAL (pg_boolean) pgIsRealFont(pg_globals_ptr globals, font_info_ptr font, pg_boolean use_alternate)
{
	pg_char_ptr			font_name;
	pg_boolean			result;

	if (use_alternate)
		font_name = font->alternate_name;
	else
		font_name = font->name;
	
	result = (pg_boolean)EnumFontFamilies((HDC)globals->machine_const, (PGSTR)&font_name[1],
							(FONTENUMPROC)enum_pgfont_proc, (LPARAM)font);
    
    return	(pg_boolean)(result == FONT_EXISTS);
}

/* Given an arbitrary point size, pgPointsizeToScreen returns what size to set
a font to achieve pointsize visual size. For example, if screen resolution is
96 dpi then a 12-point font needs to be slightly larger to look like 12 point.
Hence in this case, if pointsize = 72 this function returns 96. */

PG_PASCAL (pg_fixed) pgPointsizeToScreen (pg_ref pg, pg_fixed pointsize)
{
	paige_rec_ptr	   		pg_rec;
	pg_fixed				result;
	long					doc_resolution;
	int				     	resolution;

	pg_rec = UseMemory(pg);
	
	doc_resolution = pg_rec->port.resolution >> 16;
	resolution = (int)doc_resolution;
	result = (pg_fixed) windows_pointsize(pointsize, resolution);
	UnuseMemory(pg);
	
	return  (pgRoundFixed(result));
}


/* Given an arbitrary point size on screen, pgScreenToPointsize returns what size to set
a point size in a font really is. For example, if screen resolution is
96 dpi then a 12-point font needs to be slightly larger to look like 12 point.
Hence in this case, if screensize = 96 this function returns 72. */

PG_PASCAL (pg_fixed) pgScreenToPointsize (pg_ref pg, pg_fixed screensize)
{
	paige_rec_ptr	   	pg_rec;
	pg_fixed			result;
	long				doc_resolution;
	int				    resolution;

	pg_rec = UseMemory(pg);
	
	doc_resolution = pg_rec->port.resolution >> 16;
	resolution = (int)doc_resolution;
		
	result = (pg_fixed) windows_screensize((long)screensize, resolution);
	UnuseMemory(pg);
	
	return  (pgRoundFixed(result));
}


/* pgOSToPgColor converts an OS-specific color variable to a PAIGE color_value. For
Macintosh, *os_color is an RGBColor. For Windows, *os_color is a COLORREF. */

PG_PASCAL (void) pgOSToPgColor (const pg_plat_color_value PG_FAR *os_color, color_value_ptr pg_color)
{
    unsigned short       red, green, blue;
    
	red = GetRValue(*os_color);
	green = GetGValue(*os_color);
	blue = GetBValue(*os_color);
    
    pg_color->red = pg_color->green = pg_color->blue = pg_color->alpha = 0;

    if (red)
    	pg_color->red = (red << 8) | 0x00FF;
    if (green)
    	pg_color->green = (green << 8) | 0x00FF;
    if (blue)
    	pg_color->blue = (blue << 8) | 0x00FF;
}


/* pgColorToOS converts a PAIGE color_value to an OS-specific color variable.
For Macintosh this creates an RGBColor. For Windows this creates a COLORREF. */

PG_PASCAL (void) pgColorToOS (const color_value_ptr pg_color, pg_plat_color_value PG_FAR *os_color)
{
    *os_color = RGB(pg_color->red >> 8, pg_color->green >> 8, pg_color->blue >> 8);
}



/* pgDrawSpecialUnderline draws all underlining that is otherwise unavailable through the
OS standard styles.  The known text points are start_pt for the
beginning (with start_pt.v as the baseline) and the distance parameter as the
horizontal distance.  Note that "normal" underline might be indicated, which
IS supported by QuickDraw but is required for word underlining.

The underline type(s) to draw are indicated in draw_bits which can be any of
the bits #define at the top of this file.

The ascent is provided in case this is overline.  */

PG_PASCAL (void) pgDrawSpecialUnderline (paige_rec_ptr pg, Point from_pt,
		short distance, style_info_ptr style, short draw_bits)
{
	Point				start_pt;
	HDC				    hdc;
	HPEN			  	underline_pen, old_pen;
	COLORREF			pen_color;
	long				pen_size;
	short				ascent;
	int					pen_type;
	
	ascent = (short)style->ascent;
	start_pt = from_pt;

	if (draw_bits & OVERLINE_LINE)
		start_pt.v -= (ascent + 1);

	hdc = pgGetPlatformDevice(pg->globals->current_port);
	
	if (draw_bits & GRAY_UNDERLINE)
		pen_type = PS_DOT;
	else
		pen_type = PS_SOLID;
	
	pgColorToOS(&style->fg_color, &pen_color);
    pen_color |= pg->port.palette_select;
	pen_size = 1;
	pgScaleLong(pg->scale_factor.scale, 0, &pen_size);
	
	if (!pen_size)
		pen_size = 1;

	underline_pen = CreatePen(pen_type, (short)pen_size, pen_color);
	old_pen = SelectObject(hdc, underline_pen);

	MoveToEx(hdc, start_pt.h, start_pt.v + 1, NULL);
	LineTo(hdc, start_pt.h + distance -1, start_pt.v + 1);

	if (draw_bits & DOUBLE_UNDERLINE) {
		
		start_pt.v += (short)(pen_size * 3);
		MoveToEx(hdc, start_pt.h, start_pt.v, NULL);
		LineTo(hdc, start_pt.h + distance -1, start_pt.v);
	}
    
    SelectObject(hdc, old_pen);
    DeleteObject(underline_pen);

	pgReleasePlatformDevice(pg->globals->current_port);
}



/* trans_literate converts upper to lower or lower to upper of all chars in
text depending on do_upperase verb. */

PG_PASCAL (void) pgTransLiterate (pg_char_ptr text, long length, pg_char_ptr target,
		pg_boolean do_uppercase)
{
	register pg_char_ptr		src_ptr, target_ptr;
	register long				ctr;
	
	src_ptr = text;
	target_ptr = target;
	
	if (do_uppercase) {
	
		for (ctr = 0; ctr < length; ++ctr) {
			
			target_ptr[ctr] = src_ptr[ctr];
			if (target_ptr[ctr] >= 'a' && target_ptr[ctr] <= 'z')
				target_ptr[ctr] -= ' ';
		}
	}
	else {

		for (ctr = 0; ctr < length; ++ctr) {
			
			target_ptr[ctr] = src_ptr[ctr];
			if (target_ptr[ctr] >= 'A' && target_ptr[ctr] <= 'Z')
				target_ptr[ctr] += ' ';
		}
	}
}



/* pgEmptyRgn returns TRUE if the region is empty. */

PG_PASCAL (pg_boolean) pgEmptyRgn(pg_region rgn)
{
	RECT		r;
	
	return	(pg_boolean)(GetRgnBox(rgn, &r) == NULLREGION);
}

PG_PASCAL (void)pgRectToRgn(pg_region rgn, RECT PG_FAR *r)
{
 	SetRectRgn(rgn, r->left, r->top, r->right, r->bottom);
}
 
/* Emulates a Mac Inset region */

//¥ TRS/OITC

PG_PASCAL (void) pgInsetRgn(pg_region rgn, short dh, short dv)
{
	RgnHandle	temp;
	
	temp = pgCreateRgn();
	pgCopyRgn(rgn, temp);

	if (dh < 0)
	{
		pgOffsetRgn(temp, -dh, 0);
		pgUnionRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, dh << 1, 0);
		pgUnionRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, -dh, 0);
	}
	else if (dh > 0)
	{
		pgOffsetRgn(temp, -dh, 0);
		pgSectRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, dh << 1, 0);
		pgSectRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, -dh, 0);
	}

	if (dv < 0)
	{
		pgOffsetRgn(temp, 0, -dv);
		pgUnionRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, 0, dv << 1);
		pgUnionRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, 0, -dv);
	}
	else if (dv > 0)
	{
		pgOffsetRgn(temp, 0, -dv);
		pgSectRgn(rgn, temp, rgn);
		pgOffsetRgn(temp, 0, dv << 1);
		pgSectRgn(rgn, temp, rgn);
	}
	
	pgDisposeRgn(temp);
}

/* This function does GetDC (if access_ctr is zero) or just increments the
access counter and returns the HDC for this device. */

PG_PASCAL (HDC) pgGetPlatformDevice (graf_device_ptr the_device)
{
	HDC			dc;

	if (!the_device->access_ctr) {
		
		if (the_device->machine_var)
			the_device->machine_ref = PG_LONGWORD(long) GetDC((HWND)the_device->machine_var);
		else
			the_device->machine_ref = the_device->machine_ref3;
		
		dc = (HDC)the_device->machine_ref;

		if (the_device->palette) {
			
			the_device->previous_items[PREVIOUS_PALETTE] = PG_LONGWORD(long)SelectPalette(dc, (HPALETTE)the_device->palette, FALSE);
			RealizePalette(dc);
			the_device->palette_select = 0x02000000L;
		}
		else
			the_device->palette_select = 0;

		if (the_device->scale.scale)
			scale_dc(dc, the_device);
	}
	else
		dc = (HDC)the_device->machine_ref;

	++the_device->access_ctr;

	return 		dc;
}


/* This function does a ReleaseDC if access_ctr decrements to zero. */

PG_PASCAL (void) pgReleasePlatformDevice (graf_device_ptr the_device)
{
	if (!(--the_device->access_ctr)) {
		
		if (the_device->machine_var) {
			
			if (the_device->previous_items[PREVIOUS_FONT])
				SelectObject((HDC)the_device->machine_ref, (HANDLE)the_device->previous_items[PREVIOUS_FONT]);
			if (the_device->previous_items[PREVIOUS_PALETTE])
				SelectPalette((HDC)the_device->machine_ref, (HPALETTE)the_device->previous_items[PREVIOUS_PALETTE], FALSE);

			ReleaseDC((HWND)the_device->machine_var, (HDC)the_device->machine_ref);
			
			the_device->previous_items[PREVIOUS_FONT] = the_device->previous_items[PREVIOUS_PALETTE] = 0;
		}
	}

#ifdef PG_DEBUG
	
	if (the_device->access_ctr < 0)
		pgDebugProc(RANGE_ERR, 0);
#endif
}

/* SetFontCharWidths is a Windows-only utility that forces the character widths for a specific
style + font. Upon entry, style is the composite style (whose font is also in font_index).
The charwidths must be 256 int values. */

#ifndef UNICODE

PG_PASCAL (void) SetFontCharWidths (pg_ref pg, style_info_ptr style, int PG_FAR *charwidths)
{
	paige_rec_ptr		pg_rec;
	font_object_ptr		fonttable;
	font_info_ptr		font;
	pg_short_t			style_index;

	pg_rec = UseMemory(pg);

	if ((style_index = pgFindMatchingStyle((memory_ref) pg_rec->t_formats, style, 0,
				SIGNIFICANT_STYLE_SIZE)) > 0) {
		style_info_ptr		found_style;
		
		found_style = UseMemoryRecord(pg_rec->t_formats, (long)(style_index - 1), 0, TRUE);
		found_style->maintenance |= HAS_FORCED_WIDTHS;
		UnuseMemory(pg_rec->t_formats);
	}

	font = UseMemoryRecord(pg_rec->fonts, (long)style->font_index, 0, TRUE);
	get_windows_font_object(pg_rec, style, font);
	
	fonttable = UseMemoryRecord(pg_rec->globals->font_objects, style->machine_var2, 0, TRUE);
	pgBlockMove(charwidths, fonttable->widths, sizeof(int) * 256);

	fonttable->valid_widths |= CHARWIDTHS_COMPUTED;
	
	UnuseMemory(pg_rec->globals->font_objects);
	UnuseMemory(pg_rec->fonts);
	UnuseMemory(pg);
}
#endif


/* pgOpenPrinter sets up the target device (which will receive pgPrint drawings)
to map images correctly. For Windows we do several things. For Mac we don't
really do anything. */

PG_PASCAL (void) pgOpenPrinter (paige_rec_ptr pg_rec, graf_device_ptr print_dev,
		long first_position, rectangle_ptr page_rect)
{
	HDC						screen_dc, printer_dc;
	text_block_ptr			block;
	long					height_to_paginate, height_paginated;
    short					print_x, print_y;

	screen_dc = pgGetPlatformDevice(&pg_rec->port);
	print_dev->machine_ref4 = PG_LONGWORD(long)screen_dc;

/* Paginate the text blocks that will print to assure all font objects
are created and the line(s) are in place: */

	block = pgFindTextBlock(pg_rec, first_position, NULL, TRUE, TRUE);
	height_to_paginate = page_rect->bot_right.v - page_rect->top_left.v;
    height_paginated = 0;
    
	while (block->end < pg_rec->t_length) {

		++block;
		pgPaginateBlock(pg_rec, block, NULL, FALSE);
		height_paginated += (block->bounds.bot_right.v - block->bounds.top_left.v);
 
		if (height_paginated >= height_to_paginate)
			break;
	}

    UnuseMemory(pg_rec->t_blocks);

	printer_dc = (HDC)print_dev->machine_ref;
	
	if (!print_dev->resolution) {
		
		print_y = GetDeviceCaps(printer_dc, LOGPIXELSY);
		print_x = GetDeviceCaps(printer_dc, LOGPIXELSX);
		print_dev->resolution = print_x;
		print_dev->resolution <<= 16;
		print_dev->resolution |= print_y;
	}

/* Resolve the difference in printer resolution and force the document
to change scaling by that much: */
    
    print_x = HIWORD(print_dev->resolution);
	print_y = LOWORD(print_dev->resolution);
	set_printer_scaling(pg_rec, printer_dc, print_x, print_y);
}


/* pgPrintDeviceChanged gets called when pgSetPrintDevice() is called from the
application. For Windows we walk through and invalidate all the char widths.
For Mac we do nothing. */

PG_PASCAL (void) pgPrintDeviceChanged (paige_rec_ptr pg)
{
#ifndef UNICODE
	font_object_ptr			fonts;
	long					font_qty;

	fonts = UseMemory(pg->globals->font_objects);
	
	for (font_qty = GetMemorySize(pg->globals->font_objects); font_qty; ++fonts, --font_qty)
		fonts->valid_widths |= USE_TEMP_WIDTHS;

	UnuseMemory(pg->globals->font_objects);
#endif
}


/* pgClosePrinter undoes whatever it did in pgOpenPrinter. For Mac we do nothing.
For Windows we re-invalidate the text and restore the old mapping mode. */

PG_PASCAL (void) pgClosePrinter (paige_rec_ptr pg_rec, graf_device_ptr print_dev)
{
	HDC						printer_dc;
	
	printer_dc = (HDC)print_dev->machine_ref;
	pgReleasePlatformDevice(&pg_rec->port);

#ifndef UNICODE
	{
		font_object_ptr			fonts;
		long					font_qty;
	
		fonts = UseMemory(pg_rec->globals->font_objects);
		
		for (font_qty = GetMemorySize(pg_rec->globals->font_objects); font_qty; ++fonts, --font_qty)
			fonts->valid_widths |= (~USE_TEMP_WIDTHS);
	
		UnuseMemory(pg_rec->globals->font_objects);
	}
#endif
}



/* HandleToMemory -- converts a Handle to a memory ref of rec_size record size. */

PG_PASCAL (memory_ref) HandleToMemory (pgm_globals_ptr mem_globals, HANDLE h,
		pg_short_t rec_size)
{
	mem_rec_ptr			    mem_ptr;
	register pg_bits8_ptr	src, dest;
	long				    h_size, new_size, block_size;

	h_size = GlobalSize(h);
	new_size = (h_size / rec_size);
	block_size = (new_size * rec_size) + NON_APP_SIZE;
	
	h = GlobalReAlloc (h, block_size, 0);
    src = GlobalLock(h);
    mem_ptr = (mem_rec_ptr)src;
 
    dest = src;
    dest += sizeof(mem_rec);
    
    src += h_size;
    dest += h_size;
    pgBlockMove(src, dest, -h_size);
 	
 	mem_globals->total_unpurged += block_size;
	
	mem_ptr->rec_size = rec_size;
	mem_ptr->num_recs = mem_ptr->real_num_recs = new_size;
	mem_ptr->extend_size = 0;
	mem_ptr->access = 0;
	mem_ptr->purge = 0x08;
	mem_ptr->globals = mem_globals;
	mem_ptr->mem_id = mem_globals->current_id;

#ifdef PG_DEBUG
	{
		pg_bits8_ptr				ptr;

		ptr = (pg_bits8_ptr)mem_ptr;
		ptr += (h_size + sizeof(mem_rec));

#ifdef PG_BIG_CHECKSUMS
	    {
		long				    checksum_ctr;

			for (checksum_ctr = CHECKSUM_SIZE; checksum_ctr; --checksum_ctr)
				*ptr++ = BIG_CHECKSUM_BYTE;
		}
#else
		*ptr++ = CHECKSUM_BYTE1;
		*ptr++ = CHECKSUM_BYTE2;
		*ptr++ = CHECKSUM_BYTE3;
		*ptr++ = CHECKSUM_BYTE4;
#endif
	}
	
#endif
	
	GlobalUnlock(h);

	return	InitMemoryRef(mem_globals, h);
}


/* MemoryToHandle -- converts a memory_ref to a HANDLE.  */

PG_PASCAL (HANDLE) MemoryToHandle (memory_ref ref)
{
	HANDLE				result;
	pg_bits8_ptr		src, target;
	long				bytesize;
	
	bytesize = GetByteSize(ref);
	UseMemory(ref);			// forces a re-load
	result = (HANDLE)DetachMemory(ref);
	
	if (bytesize) {
		
		target = src = GlobalLock(result);
		src += sizeof(mem_rec);
		pgBlockMove(src, target, bytesize);
		GlobalUnlock(result);
	}

	result = GlobalReAlloc(result, bytesize, 0);
	
	return	result;
}



/* Compute if it is caret time. For PC not used for carets but is used for blinking */

PG_PASCAL (pg_boolean) pgIsCaretTime(paige_rec_ptr pg)
{
	time_t		timer, elapsed_time;
	pg_boolean	result = FALSE;

	timer = time(NULL);
	elapsed_time = timer - (time_t)pg->timer_info;
	
	if (elapsed_time >= WIN_CARET_TIME) {
		
		result = TRUE;
		blink_mode = (++blink_mode) & 1;
		pg->timer_info = (unsigned long)timer;
	}
	
	if (pg->flags & PRINT_MODE_BIT)
		blink = FALSE;
	else blink = result;
	
	return result;
}


/* Default Idle procedure - we just handle blinking character styles */

PG_PASCAL (void) pgIdleProc (paige_rec_ptr pg, short verb)
{
	style_info_ptr	info, info_1;
	long			i, count;
	pg_boolean		got_blinks = FALSE;
	
	if (verb == toggle_cursor)
	{
		if (!(pg->flags & DEACT_BITS) || ((pg->flags & DEACT_BITS) && (pg->flags2 & BLINK_ON_DEACTIVE)))
		{
			info = info_1 = UseMemory (pg->t_formats);
			count =  GetMemorySize(pg->t_formats);
			
			for (i = 0; i < count; i++, info++)
			{
				if (info->styles[blink_var])
				{
					got_blinks = TRUE;
				}
			}
			
			if (got_blinks)
			{
//				pg->procs.set_device(pg, set_pg_device, &pg->port, &pg->bk_color);
//				pgClipGrafDevice(pg, MEM_NULL, TRUE, FALSE);
			
				for (i = 0; i < count; i++, info_1++)
				{
					if (info_1->styles[blink_var])
					{
						style_run_ptr	run;
						long			j, count2;
						
						run = UseMemory (pg->t_style_run);
						count2 =  GetMemorySize(pg->t_style_run);
						
						for (j = 0; j < count2; j++, run++)
						{
							if (run->style_item == (pg_short_t)i)
							{
								long	beginning_offset, ending_offset;
								
								beginning_offset = run->offset;
								
								j++;
								run++;
								ending_offset = run->offset;
	
								pgUpdateText (pg, NULL, beginning_offset, ending_offset, MEM_NULL, NULL, best_way, TRUE);
							}
						}
						
						UnuseMemory (pg->t_style_run);
					}
				}
				
//				pg->procs.set_device(pg, unset_pg_device, &pg->port, NULL);
			}
			
			UnuseMemory (pg->t_formats);
		}
	}
}



/* pgMeasureText sets *positions to an array of character positions for data,
with the ending position (positions[length]) as the ending position.
The function returns the total width of the text. */

#ifdef WIN32_COMPILE
PG_PASCAL (pg_short_t) pgMeasureText (paige_rec_ptr pg, short measure_verb, pg_char_ptr data,
		long length, long slop, long num_spaces, pg_text_int PG_FAR *positions,
		style_walk_ptr walker)
{
	HDC						hdc;
	register int PG_FAR		*int_positions;
	register long			ctr;
	pg_char_ptr				text;
	pg_globals_ptr			globals;
	TEXTMETRIC				tm;
	pg_fixed				text_extra, space_extra;
	pg_fixed				running_space_extra, running_text_extra;
	SIZE					extent;
	pg_byte					space_char;
	short					running_width, non_roman;
	BOOL					is_double_byte;
	UINT					code_page;
	int						result, chars_fit;

	globals = pg->globals;

	hdc = pgGetPlatformDevice(globals->current_port);
	non_roman = walker->cur_font->char_type & NON_ROMAN_FLAG;
	code_page = walker->cur_font->code_page;
	SetTextJustification(hdc, 0, 0);
    GetTextMetrics(hdc, (TEXTMETRIC far*)&tm);
    space_char = (pg_char)tm.tmBreakChar;

	positions[0] = 0;

#ifndef UNICODE
	if (walker->cur_style->maintenance & HAS_FORCED_WIDTHS) {
		font_object_ptr		fonts;
		pg_short_t			index;
		int PG_FAR			*n_widths;

		int_positions = positions;
		text = data;
		fonts = UseMemoryRecord(globals->font_objects, walker->superimpose.machine_var2, 0, TRUE);
		n_widths = fonts->widths;

		for (running_width = 0, ctr = 0; ctr < length; ++int_positions, ++ctr) {
		
			index = *text++;
			*int_positions = running_width;
			running_width += n_widths[index];
		}
		
		UnuseMemory(globals->font_objects);
	}
	else
#endif
		GetTextExtentExPoint(hdc, (PGSTR)data, (int)length, 32767, &chars_fit, &positions[1], &extent);

	text_extra = walker->superimpose.char_extra;
    space_extra = (pg_fixed) 0;
	
    if (slop) {
		
		if (num_spaces)
			space_extra = pgFixedRatio((short)slop, (short)num_spaces);
		else
			space_extra = pgFixedRatio((short)slop, (short)length);
    }

	int_positions = positions;
	text = data;
    running_space_extra = running_text_extra = 0;
    result = 0;
    
	if (measure_verb == measure_textout) {

		for (ctr = 0; ctr < length; ++int_positions, ++text, ++ctr) {
			
			if (non_roman && (ctr < (length - 1)))
				is_double_byte = FirstMultiCharByte(code_page, *text);
			else
				is_double_byte = FALSE;

			if (is_double_byte)
				*int_positions = int_positions[2] - int_positions[0];
			else 	
				*int_positions = int_positions[1] - int_positions[0];

			if (text_extra) {

			    running_text_extra += text_extra;
			    running_width = (short)(HIWORD(running_text_extra));
			    running_text_extra &= 0x0000FFFF;
			    *int_positions += running_width;
			}

			if ((!num_spaces || (*text == space_char)) && slop) {
			    
			    running_space_extra += space_extra;
				if (is_double_byte)
					running_space_extra += space_extra;	
			    running_width = (short)(HIWORD(running_space_extra));
			    running_space_extra &= 0x0000FFFF;
			    *int_positions += running_width;
			}
		
			result += *int_positions;

			if (is_double_byte) {
				int_positions[1] = int_positions[0];
				*int_positions = 0;
				int_positions += 1;
				text += 1;
				ctr += 1;
			}
		}
	}
	else {

    	ctr = 0;
		
		if (text_extra || slop) {
		
			for (running_width = 0; ctr < length; ++int_positions, ++text, ++ctr) {
	
				if (non_roman && (ctr < (length - 1)))
					is_double_byte = FirstMultiCharByte(code_page, *text);
				else
					is_double_byte = FALSE;

				*int_positions += running_width;
				
				if (is_double_byte)
					int_positions[1] += running_width;

				if (text_extra) {

				    running_text_extra += text_extra;
				    running_width += (short)(HIWORD(running_text_extra));
				    running_text_extra &= 0x0000FFFF;
				}
				
				if ((!num_spaces || (*text == (pg_char)space_char)) && slop) {
				    
				    running_space_extra += space_extra;
					if (is_double_byte)
						running_space_extra += space_extra;
				    running_width += (short)(HIWORD(running_space_extra));
				    running_space_extra &= 0x0000FFFF;
				}

				if (is_double_byte) {

					int_positions += 1;
					ctr += 1;
					text += 1;
				}
			}
		
			*int_positions += running_width;
			result = *int_positions;
		}
	}

	pgReleasePlatformDevice(globals->current_port);
	
	return	result;
}
#else
// WIN16_COMPILE
PG_PASCAL (pg_short_t) pgMeasureText (paige_rec_ptr pg, short measure_verb, pg_char_ptr data,
		long length, long slop, long num_spaces, pg_text_int PG_FAR *positions,
		style_walk_ptr walker)
{
	register int     PG_FAR  		*int_positions;
	register pg_char_ptr	    	text;
	register int			  		running_width;
	register long			   		ctr;
	register pg_fixed		        text_extra, running_text_extra;
	pg_fixed						running_space_extra, space_extra, overhang;
	font_object_ptr					fonts;
	pg_globals_ptr			  		globals;
	TEXTMETRIC			      		tm;
	HDC						     	hdc;
	BOOL							is_double_byte;
	pg_short_t				      	index;
	UINT							code_page;
	static int						temp_widths[256];
	int PG_FAR			    		*n_widths;
	long							full_width;
	int								result;
	short					   		non_roman, space_char;

	globals = pg->globals;
	
	hdc = pgGetPlatformDevice(globals->current_port);
	
	non_roman = walker->cur_font->char_type & MULTIBYTE_FLAG;
	code_page = walker->cur_font->code_page;

    GetTextMetrics(hdc, (TEXTMETRIC far*)&tm);
    overhang = (pg_fixed)tm.tmOverhang;
    overhang <<= 16;

	fonts = UseMemoryRecord(globals->font_objects, walker->superimpose.machine_var2, 0, TRUE);
	
	n_widths = fonts->widths;
	
	if (!(fonts->valid_widths & CHARWIDTHS_COMPUTED)) {
	    
		GetCharWidth(hdc, 0, 255, n_widths);
		n_widths[globals->soft_line_char] = 0;
		n_widths[globals->line_wrap_char] = 0;		
		fonts->valid_widths |= CHARWIDTHS_COMPUTED;
	}
	else
	if (fonts->valid_widths & USE_TEMP_WIDTHS) {
		
		n_widths = temp_widths;
		GetCharWidth(hdc, 0, 255, n_widths);
		n_widths[globals->soft_line_char] = 0;
		n_widths[globals->line_wrap_char] = 0;		
	}

	text_extra = walker->superimpose.char_extra;
    space_extra = (pg_fixed) 0;
    space_char = (short)tm.tmBreakChar;
	
    if (slop) {
		
		if (num_spaces)
			space_extra = pgFixedRatio((short)slop, (short)num_spaces);
		else
			space_extra = pgFixedRatio((short)slop, (short)length);
    }

	int_positions = positions;
	text = data;
    running_space_extra = running_text_extra = 0;
    result = 0;
    
	if (measure_verb == measure_textout) {

		for (ctr = 0; ctr < length; ++int_positions, ++ctr) {
			
			if (non_roman && (ctr < (length - 1)))
				is_double_byte = FirstMultiCharByte(code_page, *text);
			else
				is_double_byte = FALSE;
			
			if (is_double_byte) {
				
				full_width = GetTextWidth(hdc, (PGSTR)text, 2);
				
				if (text_extra) {
				
					running_text_extra += text_extra;
				    running_width = (short)(HIWORD(running_text_extra));
				    running_text_extra &= 0x0000FFFF;
				    full_width += running_width;
				}
				
				if (!num_spaces && slop) {
				    
				    running_space_extra += (space_extra + space_extra);
				    running_width = (short)(HIWORD(running_space_extra));
				    running_space_extra &= 0x0000FFFF;
				    full_width += running_width;
				}
				
				*int_positions = 0;		   		// sdr our anti-aliasing engine expects double byte chars to have first byte 0
				int_positions[1] = (int)full_width;	// sdr and the second byte contains the width of the double byte character
				++int_positions;

				++ctr;
				text += 2;
				result += (int)full_width;
			}
			else {

				index = *text++;
				*int_positions = n_widths[index];
				
				if (text_extra) {

				    running_text_extra += text_extra;
				    running_width = (short)(HIWORD(running_text_extra));
				    running_text_extra &= 0x0000FFFF;
				    *int_positions += running_width;
				}

				if ((!num_spaces || (index == (pg_short_t)space_char)) && slop) {
				    
				    running_space_extra += space_extra;
				    running_width = (short)(HIWORD(running_space_extra));
				    running_space_extra &= 0x0000FFFF;
				    *int_positions += running_width;
				}
			
				result += *int_positions;
			}
		}
	}
	else {

    	ctr = 0;
 		text_extra -= overhang;

		for (running_width = 0; ctr < length; ++int_positions, ++ctr) {

			if (non_roman && (ctr < (length - 1)))
				is_double_byte = FirstMultiCharByte(code_page, *text);
			else
				is_double_byte = FALSE;
			
			if (is_double_byte) {
				int				full_width;

				*int_positions++ = running_width;
				full_width = GetTextWidth(hdc, (LPSTR)text, 2);
				
				text_extra += overhang;

				if (text_extra) {

				    running_text_extra += (text_extra);
				    full_width += (short)(HIWORD(running_text_extra));
				    running_text_extra &= 0x0000FFFF;
				}
				
				text_extra -= overhang;

				if (!num_spaces && slop) {
				    
				    running_space_extra += (space_extra + space_extra);
				    full_width += (short)(HIWORD(running_space_extra));
				    running_space_extra &= 0x0000FFFF;
				}

				running_width += full_width;
				*int_positions = running_width - (full_width / 2);

				++ctr;
				text += 2;
			}
			else {
			
				index = *text++;
				*int_positions = running_width;
				running_width += n_widths[index];
				
				if (text_extra) {

				    running_text_extra += text_extra;
				    running_width += (short)(HIWORD(running_text_extra));
				    running_text_extra &= 0x0000FFFF;
				}
				
				if ((!num_spaces || (index == (pg_short_t)space_char)) && slop) {
				    
				    running_space_extra += space_extra;
				    running_width += (short)(HIWORD(running_space_extra));
				    running_space_extra &= 0x0000FFFF;
				}
			}
		}
	
		*int_positions = result = running_width;
	}

	pgReleasePlatformDevice(globals->current_port);

	UnuseMemory(globals->font_objects);
	
	return	result;
}
#endif


/********************************** LOCAL FUNCTIONS ***************************/

/* enum_pgfont_proc is the function we use to verify the existence of a font AND to initialize
the miscellaneious required info. */

int CALLBACK enum_pgfont_proc (LOGFONT far *log, TEXTMETRIC far *tm, int font_type, LPARAM pg_font)
{
	font_info_ptr			user_font;

	user_font = (font_info_ptr)pg_font;

    if (!user_font->machine_var[PG_OUT_PRECISION])
    	user_font->machine_var[PG_OUT_PRECISION] = log->lfOutPrecision;
    if (!user_font->machine_var[PG_CLIP_PRECISION])
    	user_font->machine_var[PG_CLIP_PRECISION] = log->lfClipPrecision;
    if (!user_font->machine_var[PG_QUALITY])
    	user_font->machine_var[PG_QUALITY] = log->lfQuality;
    if (!user_font->machine_var[PG_CHARSET])
    	user_font->machine_var[PG_CHARSET] = log->lfCharSet;

	return	FONT_EXISTS;
}



/* This initializes the four-byte global "char" (note that the 4 bytes are
already initialized to zeros). This function is used for DEFAULTS, which assumes
single byte values! */

static void init_global_char(pg_char_ptr global_char, pg_short_t value)
{
#ifdef GLOBALS_ARE_PSTRINGS
	global_char[0] = 1;
	global_char[1] = (pg_char)value;
#else                               
	global_char[0] = (pg_char)value;
#endif
}


/* This returns a 16-bit value from a global char string */

static pg_short_t global_char_to_int (pg_char_ptr global_char)
{
	register pg_short_t	     result_16;

#ifdef GLOBALS_ARE_PSTRINGS

	result_16 = global_char[1];
	if (global_char[0] > 1) {
		
		result_16 <<= 8;
		result_16 |= global_char[2];
	}

#else
	
	result_16 = global_char[0];
	if (global_char[1]) {
		result_16 <<= 8;
		result_16 |= global_char[1];
	}

#endif

	return  result_16;
}


/* This returns the size of the global char (either 0, 1 or 2). */

static short global_char_size (pg_char_ptr global_char)
{
#ifdef GLOBALS_ARE_PSTRINGS
	return  global_char[0];
#else
	if (!global_char[0])
		return  0;
	if (!global_char[1])
		return  1;
	return  2;
#endif
}


/* This functions must build a graf_device record based on the single device
pointer in *port */

static void make_graf_device (pg_globals_ptr globals, generic_var port, generic_var machine_ref,
	 	graf_device_ptr device)
{
	pgFillBlock(device, sizeof(graf_device), 0);
	device->machine_ref3 = globals->machine_const;  // = default DC

	device->machine_var = port;
	
	if ((device->machine_ref = machine_ref) != 0)
		++device->access_ctr;   /* This assumes HDC already valid. */
	
	device->graf_stack = MemoryAlloc(globals->mem_globals,
			sizeof(port_preserve), 0, 4);
	SetMemoryPurge(device->graf_stack, NO_PURGING_STATUS, FALSE);
	device->bk_color = globals->def_bk_color;

	device->resolution = 0x00600060;
	
	if (device->machine_var || device->machine_ref) {
		long		y_axis, x_axis;
		HDC			hdc;
		
		hdc = pgGetPlatformDevice(device);
		y_axis = (long)GetDeviceCaps(hdc, LOGPIXELSY);
		x_axis = (long)GetDeviceCaps(hdc, LOGPIXELSX);
		pgReleasePlatformDevice(device);
		
		device->resolution = (y_axis << 16) | x_axis;
	}
	
	device->clip_rgn = pgCreateRgn();
	device->clip_info.change_flags = -1;
}


/* This function draws the caret in XOR mode */

static void draw_cursor (paige_rec_ptr pg, t_select_ptr select, short verb)
{
	point_start_ptr		starts;
	style_info_ptr		caret_style;
	text_block_ptr		block;
	rectangle			wrap_rect;
	co_ordinate			caret_top, caret_bot, scroll_adjust, repeat_offset;
	Point				top_caret, bottom_caret;
	long			    caret_height, style_offset, r_num;
	long			    container_proc_refcon;
	short			   	top_height, bottom_height;

	if (select->flags & SELECTION_DIRTY)
		pgCalcSelect(pg, select);

	block = pgFindTextBlock(pg, select->offset, NULL, TRUE, TRUE);
	starts = UseMemoryRecord(block->lines, select->line, USE_ALL_RECS, TRUE);
	
	container_proc_refcon = 0;
	scroll_adjust = pg->scroll_pos;
	pgNegatePt(&scroll_adjust);
	pgAddPt(&pg->port.origin, &scroll_adjust);
	r_num = starts->r_num;

	pgCallContainerProc(pg, (pg_short_t)r_num, &scroll_adjust, clip_container_verb,
			 &pg->port.scale, &container_proc_refcon); 

	if (style_offset = select->offset)
		--style_offset;

	caret_style = pgFindTextStyle(pg, style_offset);

	caret_top.h = starts->bounds.top_left.h + select->primary_caret;
	caret_height = caret_style->ascent + caret_style->descent;
	caret_top.v = starts->bounds.bot_right.v - starts->baseline - caret_style->ascent;
	caret_bot.h = starts->bounds.top_left.h + select->secondary_caret;
	caret_bot.v = caret_top.v;
	
	caret_top.h -= 1;
	caret_bot.h -= 1;

	GetMemoryRecord(pg->wrap_area, pgGetWrapRect(pg, starts->r_num, &repeat_offset) + 1, &wrap_rect);
	pgOffsetRect(&wrap_rect, repeat_offset.h, 0);
	
	if (caret_top.h >= wrap_rect.bot_right.h)
		caret_top.h = wrap_rect.bot_right.h - 1;
	if (caret_bot.h >= wrap_rect.bot_right.h)
		caret_bot.h = wrap_rect.bot_right.h - 1;

	if (caret_top.h < wrap_rect.top_left.h)
		caret_top.h = wrap_rect.top_left.h;
	if (caret_bot.h < wrap_rect.top_left.h)
		caret_bot.h = wrap_rect.top_left.h;

	UnuseMemory(pg->t_formats);

	pgSubPt(&pg->scroll_pos, &caret_top);
	pgAddPt(&pg->port.origin, &caret_top);
	pgSubPt(&pg->scroll_pos, &caret_bot);
	pgAddPt(&pg->port.origin, &caret_bot);

	pgScalePt(&pg->port.scale, NULL, &caret_top);
	pgScalePt(&pg->port.scale, NULL, &caret_bot);

	pgScaleLong(pg->port.scale.scale, 0, &caret_height);

	top_height = (short)caret_height;
	top_height /= 2;
	bottom_height = (short)(caret_height - top_height - 1);

	top_caret.h = pgLongToShort(caret_top.h);
	top_caret.v = pgLongToShort(caret_top.v);

	bottom_caret.h = pgLongToShort(caret_bot.h);
	bottom_caret.v = pgLongToShort(caret_bot.v);
	bottom_caret.v += (top_height + 1);

	UnuseMemory(block->lines);
	UnuseMemory(pg->t_blocks);

    top_caret.v = (short)((bottom_caret.v + bottom_height) - caret_height);
 	
	switch (verb) 
	{   
		case toggle_cursor:	     
		case toggle_cursor_idle:
		case restore_cursor:
		case show_cursor:
		case activate_cursor:
			if (!(pg->flags & CARET_BIT)) {
			
				if (create_caret(pg, (short)top_caret.h, (short)top_caret.v, (short)caret_height)) {
				
					SetCaretPos(top_caret.h, top_caret.v);	    
					ShowCaret((HWND)pg->port.machine_var);
					pg->flags |= CARET_BIT;
				}  
			}

			break;
	
		case deactivate_cursor:
		case hide_cursor:
			if (pg->flags & CARET_CREATED_BIT)
				DestroyCaret();
			
			pg->flags &= (long)(~(CARET_BIT | CARET_CREATED_BIT));

			break;
		
		case update_cursor:
			// do nothing
			break;
		
		case compute_cursor:
 			if (!(pg->stable_caret.h = top_caret.h))
 				++pg->stable_caret.h;

 			pg->stable_caret.v = top_caret.v + 1;
			
			break;
	}

	pgCallContainerProc(pg, (pg_short_t)r_num, (co_ordinate_ptr)NULL, unclip_container_verb, (pg_scale_ptr)NULL, &container_proc_refcon); 
}



/* This is the machine-dependent function to set all text fonts & styles. The
window defined in globals->current_port is affected.
Windows note: The font object is in style->machine_var, or it is created if
style->machine_var is zero.  */

static void install_machine_font (paige_rec_ptr pg, style_info_ptr style,
		font_info_ptr font, pg_boolean include_offscreen)
{
	register style_info_ptr	the_style = style;
	pg_globals_ptr			globals;
	HDC		     			hdc;
	COLORREF				text_color;
	HANDLE					previous_font;

	globals = pg->globals;
	hdc = pgGetPlatformDevice(globals->current_port);

// Fix MM reported:

	previous_font = SelectObject(hdc, (HANDLE) the_style->machine_var);
	
	if (globals->current_port->previous_items[PREVIOUS_FONT] == 0)
		globals->current_port->previous_items[PREVIOUS_FONT] = PG_LONGWORD(long)previous_font;

	pgColorToOS(&the_style->fg_color, (void PG_FAR *)&text_color);
	text_color |= globals->current_port->palette_select;
	SetTextColor(hdc, text_color);
	SetTextJustification(hdc, 0, 0);
 
	pgReleasePlatformDevice(globals->current_port);
}


/* scale_dc scales the device context to match the PAIGE scaling factor. */

static void scale_dc (HDC dc, graf_device_ptr the_device)
{
	long			resolution, scaled_resolution;
			
	resolution = GetDeviceCaps(dc, LOGPIXELSX);
	scaled_resolution = resolution;
	pgScaleLong(-the_device->scale.scale, 0, &resolution);

	SetMapMode(dc, MM_ISOTROPIC);
	SetWindowExtEx(dc, (short)resolution, (short)resolution, NULL);
	SetViewportExtEx(dc, (short)scaled_resolution, (short)scaled_resolution, NULL);

    resolution = GetDeviceCaps(dc, LOGPIXELSX);
}


/* get_unscaled_dc returns the DC forced to unscaled mode. */

static HDC get_unscaled_dc (paige_rec_ptr pg, short PG_FAR *index)
{
	HDC			result;
	
	if (pg->port.access_ctr)
		*index = SaveDC((HDC)pg->port.machine_ref);
	else
		*index = 0;
	
	result = pgGetPlatformDevice(&pg->port);
	unset_printer_scaling(result);
	
	return		result;
}


/* restore_scaled_dc returns the DC to its original state, after
calling get_unscaled_dc. */

static void restore_scaled_dc (paige_rec_ptr pg, short index)
{
 	pgReleasePlatformDevice(&pg->port);
 	
 	if (pg->port.access_ctr)
 		RestoreDC((HDC)pg->port.machine_ref, index);
}

			
/* This draws special symbol the_char unless the_char is zero.  */

static void draw_special_char (paige_rec_ptr pg, pg_char_ptr the_char, co_ordinate_ptr point)
{
	if (the_char) {

#ifndef GLOBALS_ARE_PSTRINGS
		TextOut((HDC)pg->globals->current_port->machine_ref, (short) point->h, (short) point->v,
				(PGSTR) the_char, global_char_size(the_char));
#else
        TextOut((HDC)pg->globals->current_port->machine_ref, (short) point->h, (short) point->v,
				(PGSTR) &the_char[1], global_char_size(the_char));
#endif

	}
}



/* This sets the invisible char style, also scales point size if necessary.
The function returns the ascent of the new font. */

static short set_invisible_symbol_style (paige_rec_ptr pg)
{
	HDC						hdc;
	TEXTMETRIC				tm;
	style_info		      	the_style;
	font_info		      	the_font;
	pg_boolean		      	offscreen_included;
	long		    		invis_pointsize;
	
	invis_pointsize = SPECIAL_SYMBOL_TXSIZE;
	pgScaleLong(pg->scale_factor.scale, 0, &invis_pointsize);

	offscreen_included = (pg->globals->offscreen_port.machine_ref != 0);

	pgBlockMove(&pg->globals->def_style, &the_style, sizeof(style_info));
	pgBlockMove(&pg->globals->def_font, &the_font, sizeof(font_info));
	the_style.point = (invis_pointsize << 16);
     		
	if (!pg->globals->invis_font) {
	
	    the_style.procs.init(pg, &the_style, &the_font);
        get_windows_font_object(pg, &the_style, &the_font);
        pg->globals->invis_font = the_style.machine_var;
    }

	the_style.machine_var = pg->globals->invis_font;
	the_style.procs.install(pg, &the_style, &the_font, NULL, offscreen_included);
    
    hdc = pgGetPlatformDevice(&pg->port);
    GetTextMetrics(hdc, &tm);
    pgReleasePlatformDevice(&pg->port);
    
    return	(short)tm.tmAscent;
}



/* draw_box_style draws a frame around the current line. It will not draw the
left edge if the previous style is also boxed (and we are not at beginning of
a line), nor will it draw the right side if next style is also boxed (and we
are not at end of line). */


static void draw_box_style (paige_rec_ptr pg, style_walk_ptr walker,
		draw_points_ptr draw_position)
{
	register style_info_ptr			style_base;
	rectangle						box_frame;
	Rect							frame;
	point_start_ptr					line_start;
	pg_short_t						style_index;
	pg_boolean						draw_edge;
	long							line_width, descent, next_offset;
	long							original_walker;
	HDC								hdc;
	
	hdc = (HDC)pg->globals->current_port->machine_ref;
	SelectObject(hdc, GetStockBrush(BLACK_BRUSH));

	line_start = draw_position->starts;
	style_base = walker->style_base;
	original_walker = walker->current_offset;
	next_offset = line_start->offset;
	next_offset += draw_position->block->begin;
	pgSetWalkStyle(walker, next_offset);

	descent = pgGetLineRealDescent(draw_position->block, line_start, walker);
	pgScaleLong(pg->scale_factor.scale, 0, &descent);

	box_frame.top_left = box_frame.bot_right = draw_position->from;
	box_frame.top_left.v -= draw_position->ascent;
	box_frame.bot_right.v += descent;
	line_width = line_start->bounds.bot_right.h - line_start->bounds.top_left.h;
	pgScaleLong(pg->scale_factor.scale, 0, &line_width);
	box_frame.bot_right.h = box_frame.top_left.h + line_width;
	
	RectangleToRect(&box_frame, NULL, &frame);

	MoveToEx(hdc, frame.left, frame.top, NULL);
	LineTo(hdc, frame.right - 1, frame.top);
	MoveToEx(hdc, frame.left, frame.bottom - 1, NULL);
	LineTo(hdc, frame.right - 1, frame.bottom - 1);

	draw_edge = TRUE;

	if (!(line_start->flags & NEW_LINE_BIT))
		if (walker->current_offset == walker->prev_style_run->offset) {
		
		if (walker->prev_style_run->offset) {
			
			--walker->prev_style_run;
			style_index = walker->prev_style_run->style_item;
			++walker->prev_style_run;
			
			draw_edge = (style_base[style_index].styles[boxed_var] == 0);
		}
	}

	if (draw_edge) {

		MoveToEx(hdc, frame.left, frame.top, NULL);
		LineTo(hdc, frame.left, frame.bottom - 1);
	}
	
	draw_edge = TRUE;

	if (!(line_start->flags & LINE_BREAK_BIT))
		if (walker->next_style_run->offset == next_offset) {
		
		if (walker->next_style_run->offset < pg->t_length) {
			
			style_index = walker->next_style_run->style_item;
			draw_edge = (style_base[style_index].styles[boxed_var] == 0);
		}
	}

	if (draw_edge) {

		MoveToEx(hdc, frame.right - 1, frame.top, NULL);
		LineTo(hdc, frame.right - 1, frame.bottom - 1);
	}

	pgSetWalkStyle(walker, original_walker);
}



/* This function sets a default font name if the name is null(s)  */

static void init_def_font_name (paige_rec_ptr pg, font_info_ptr font)
{
	HDC		     hdc;
	
	hdc = pgGetPlatformDevice(&pg->port);
	GetTextFace(hdc, FONT_SIZE - 2, &font->name[1]);
	font->name[0] = (pg_char)pgCStrLength((pg_c_string_ptr)&font->name[1]);

	pgReleasePlatformDevice(&pg->port);
}


/* set_alternate_pointsize changes the current font based on a different
resolution (in new_resolution). */

static void set_alternate_pointsize (paige_rec_ptr pg, long new_resolution,
		style_walk_ptr walker)
{
	long			old_resolution;
    
    old_resolution = pg->resolution;
    pg->resolution = new_resolution << 16;
    pg->resolution |= new_resolution;
	get_windows_font_object(pg, walker->cur_style, walker->cur_font);
	walker->cur_style->procs.install(pg, walker->cur_style, walker->cur_font, &walker->superimpose, TRUE);
	pg->resolution = old_resolution;
}


/* pgMultiplyFixed multiplies two fixed decimal numbers (a fixed decimal is a

long whose high-order word is the integer and low-order word the fraction. Hence,
0x00018000 = 1.5).   */

PG_PASCAL (pg_fixed) pgMultiplyFixed (pg_fixed fix1, pg_fixed fix2)
{
	pg_fixed	arg1, arg2;
	pg_boolean	negate;
	long		lo1, hi1, lo2, hi2, product;
	
	if (!fix1 || !fix2)
		return	(pg_fixed)0;

	negate = FALSE;

	if ((arg1 = (long)fix1) < 0) {
	
		arg1 = (long)-fix1;
		negate ^= 1;
	}
	
	if ((arg2 = (long)fix2) < 0) {
	
		arg2 = (long)-fix2;
		negate ^= 1;
	}
		
	lo1 = (long)arg1 & 0xFFFF;
	hi1 = (long)arg1 >> 16;
	lo2 = (long)arg2 & 0xFFFF;
	hi2 = (long)arg2 >> 16;
	
	product = ((lo1 * lo2) >> 16) + (lo1 * hi2) + (hi1 * lo2) + ((hi1 * hi2) << 16);
	
	if (negate)
		product = -product;

	return (pg_fixed)product;
}


/* pgDivideFixed divides fixed number fix1 into fix2 (a fixed decimal is a

long whose high-order word is the integer and low-order word the fraction. Hence,
0x00018000 = 1.5).   */

PG_PASCAL (pg_fixed) pgDivideFixed (pg_fixed fix1, pg_fixed fix2)
{
	long    fix_long;
	
	if (!fix2)
		return	0;

	fix_long = (fix1 << 16) / fix2;
	
	return fix_long;
}


/* pgFixedRatio returns a fixed number which is the ratio of n / d (a fixed decimal is a

long whose high-order word is the integer and low-order word the fraction. Hence,
0x00018000 = 1.5).   */

PG_PASCAL (pg_fixed) pgFixedRatio (short n, short d)
{
	long	    result;

	result = (long) n;
	
	result = ((pg_fixed) (result << 16) / d);      
	return  result;
}


/* get_windows_font_object first checks to see if a matching font object
exists in globals and, if so, it is returned. If it does not exist, one is created.
Note, this only gets called at style initialization time. */

static void get_windows_font_object (paige_rec_ptr pg, style_info_ptr the_style, font_info_ptr font)
{
	pg_globals_ptr		globals;
	LOGFONT	 			log_font;
	font_object_ptr		available_fonts;
	long	    		pointsize, resolution, widthsize, num_fonts, font_ctr;
    
    globals = pg->globals;
	pgFillBlock(&log_font, sizeof(LOGFONT), 0);
	
	pointsize = pgComputePointSize(pg, the_style);
	widthsize = the_style->char_width >> 16;
	resolution = pg->resolution >> 16;	// Document base resolution

	log_font.lfHeight =  -MulDiv((int)pointsize, (int)resolution, 72);

	log_font.lfWidth = (short)widthsize;
	
	if (the_style->styles[bold_var])
		log_font.lfWeight = FW_BOLD;
	else
		log_font.lfWeight = FW_NORMAL;

    if (the_style->styles[italic_var])
		log_font.lfItalic = 1;
	if (the_style->styles[underline_var])
	log_font.lfUnderline = 1;
    if (the_style->styles[strikeout_var])
	log_font.lfStrikeOut = 1;

    log_font.lfOutPrecision = (BYTE)font->machine_var[PG_OUT_PRECISION];
    log_font.lfClipPrecision = (BYTE)font->machine_var[PG_CLIP_PRECISION];
    log_font.lfQuality = (BYTE)font->machine_var[PG_QUALITY];
    if (!(log_font.lfPitchAndFamily = (BYTE)font->family_id))
    	log_font.lfPitchAndFamily = FF_DONTCARE | DEFAULT_PITCH;
	log_font.lfCharSet = (BYTE)font->machine_var[PG_CHARSET];
	
	if (font->environs & FONT_USES_ALTERNATE)
		lstrcpy(log_font.lfFaceName, &font->alternate_name[1]);
	else
    	lstrcpy(log_font.lfFaceName, &font->name[1]);

 // See if the font exists:
 
 	num_fonts = GetMemorySize(globals->font_objects);
 	available_fonts = UseMemory(globals->font_objects);
 	
 	for (font_ctr = 0; font_ctr < num_fonts; ++font_ctr) {
 		
 		if (pgEqualStruct(&log_font, &available_fonts->log, sizeof(LOGFONT))) {
 			
 			the_style->machine_var = available_fonts->the_font;
 			the_style->machine_var2 = font_ctr;
 			UnuseMemory(globals->font_objects);
 			
 			return;
 		}
 
 		++available_fonts;
 	}
 	
 	UnuseMemory(globals->font_objects);
 
	the_style->machine_var = PG_LONGWORD(long) CreateFontIndirect(&log_font);
	the_style->machine_var2 = GetMemorySize(globals->font_objects);
	
	available_fonts = AppendMemory(globals->font_objects, 1, TRUE);
	available_fonts->the_font = the_style->machine_var;
	available_fonts->style_ptsize = pointsize;
	pgBlockMove(&log_font, &available_fonts->log, sizeof(LOGFONT));
	
	UnuseMemory(globals->font_objects);
}


/* windows_pointsize returns what the pointsize should be based on resolution */

static pg_fixed windows_pointsize (pg_fixed ptsize, int resolution)
{
	pg_fixed	point_ratio;

	if (point_ratio = pgFixedRatio((short)resolution, (short)72))
		return  pgMultiplyFixed(point_ratio, ptsize);
	
	return  ptsize;
}


/* windows_screensize returns the opposite of windows_pointsize */

static pg_fixed windows_screensize (pg_fixed screensize, int resolution)
{
	pg_fixed	point_ratio;

	if (!resolution)
		return  screensize;

	if (point_ratio = pgFixedRatio((short)72, (short)resolution))
		return  pgMultiplyFixed(point_ratio, screensize);
	
	return  screensize;
}


/* pgSystemTextDirection must return the default writing justification, which
should be one of the enums for direction (except system_direction). */

PG_PASCAL (short) pgSystemDirection (pg_globals_ptr globals)
{
#ifdef WIN_MULTILINGUAL
	WORD	sys_lang;

	if ((sys_lang = GetSystemDefaultLangID()) == LANG_ARABIC || sys_lang == LANG_HEBREW)
		return  right_left_direction;
#endif
	return  left_right_direction;
}


/* create_caret creates a new caret if not created already, then
returns TRUE if a caret was indeed created. Initially, the caret is
not shown.  */

static pg_boolean create_caret (paige_rec_ptr pg, short h_loc, short v_loc, short caret_size)
{
	rectangle_ptr		vis_ptr;
	long				top_loc, bottom_loc, left_loc, right_loc;

	if (pg->port.caret_info != (long) caret_size) {
		
		if (pg->flags & CARET_CREATED_BIT)
			DestroyCaret();
		
		pg->flags &= (long) (~(CARET_BIT | CARET_CREATED_BIT));
	}
	
    vis_ptr = UseMemory(pg->vis_area);
    left_loc = right_loc = h_loc - pg->port.origin.h;
    ++right_loc;
    top_loc = bottom_loc = v_loc - pg->port.origin.v;
    bottom_loc += caret_size;
 	
	if (!(pg->flags & (long)CARET_CREATED_BIT)) {
	
		CreateCaret((HWND)pg->port.machine_var, (HBITMAP)MEM_NULL,
				pg->doc_info.caret_width_extra + 1, (int)caret_size);
		pg->flags |= CARET_CREATED_BIT;
		pg->flags &= (~INVALID_CURSOR_BIT);
	}

	pg->port.caret_info = caret_size;
	
	UnuseMemory(pg->vis_area);

	return	TRUE;
}


/* build_background_brush creates a BRUSH object for erasing the background. */

static HBRUSH build_background_brush (graf_device_ptr device)
{
	LOGBRUSH		brush;
	
	brush.lbStyle = BS_SOLID;
	pgColorToOS(&device->bk_color, (void PG_FAR *)&brush.lbColor);
	brush.lbColor |= device->palette_select;
	brush.lbHatch = 0;

	return	CreateBrushIndirect(&brush);
}


/* set_space_extra sets the "justify" value of the text. If space_char is a null
char then the break character is determined. */

static int get_space_extra (HDC hdc, pg_char_ptr text, short text_length, pg_char space_char)
{
	register	pg_char_ptr	  data;
	register	short		  t_length, num_brks;
	register	pg_char		  brk_char;
	TEXTMETRIC				  metrics;
	
	if (!(brk_char = space_char)) {
		GetTextMetrics(hdc, &metrics);
		brk_char = (pg_char) metrics.tmBreakChar;
    }
    
	data = text;
	
	for (t_length = num_brks = 0; t_length < text_length; ++t_length)
	    if (data[t_length] == brk_char)
		++num_brks;
    
    return      (int) num_brks;
}


/* set_printer_scaling sets up the correct mode in the printer DC. If use_x
and use_y are non-zero we use those values for the printer DC resolution. */

static void set_printer_scaling (paige_rec_ptr pg, HDC printer_dc, short use_x, short use_y)
{
	short				print_resolution_x, screen_resolution_x;
	short				print_resolution_y, screen_resolution_y;

	if (!(print_resolution_x = use_x))
		print_resolution_x = GetDeviceCaps(printer_dc, LOGPIXELSX);
	if (!(print_resolution_y = use_y))
		print_resolution_y = GetDeviceCaps(printer_dc, LOGPIXELSY);

	screen_resolution_x = HIWORD(pg->resolution);
	screen_resolution_y = LOWORD(pg->resolution);

	SetMapMode(printer_dc, MM_ISOTROPIC);
	SetWindowExtEx(printer_dc, screen_resolution_x, screen_resolution_y, NULL);
	SetViewportExtEx(printer_dc, print_resolution_x, print_resolution_y, NULL);
}


/* unset_printer_scaling restores normal mode in the printer_dc. */

static void unset_printer_scaling (HDC printer_dc)
{
	SetMapMode(printer_dc, MM_TEXT);
	SetWindowExtEx(printer_dc, 1, 1, NULL);
	SetViewportExtEx(printer_dc, 1, 1, NULL);
}

/* extra_print_descent figures out the difference between the screen font and printer font
in terms of descent. This is only called during printing. */

static short extra_print_descent (paige_rec_ptr pg, HDC current_dc, style_info_ptr style)
{
	HANDLE			old_font;
	TEXTMETRIC		printer_tm, screen_tm;
	HDC				screen_dc;
	
	if (!pg->port.machine_ref4)
		return	0;
	
	screen_dc = (HDC)pg->port.machine_ref4;

	GetTextMetrics(current_dc, &printer_tm);
	old_font = SelectObject(screen_dc, (HANDLE)style->machine_var);
	GetTextMetrics(screen_dc, &screen_tm);
	SelectObject(screen_dc, old_font);
	
	return	(short)(screen_tm.tmDescent - printer_tm.tmDescent);
}


// Win32 & NT specific

#ifdef WIN32_COMPILE
short GetTextWidth (HDC hdc, PGSTR str, int length)
{
	SIZE			lpSize;

	GetTextExtentPoint32(hdc, str, (int)length, &lpSize);
	return	(short)lpSize.cx;
}
#endif



/* This function returns TRUE if the character at the given offset is the last
of a particular World script run. The offset param is a global (absolute) offset. */

static pg_boolean last_char_of_script (style_walk_ptr walker, long offset)
{
	register style_run_ptr	  run;
	register style_info_ptr	 info;
	short				 	 font_index;

	if (!offset)
		return  FALSE;

	run = walker->next_style_run;
	if (!run)
		return FALSE;

	if ((offset + (long)1) == run->offset) {

		info = walker->style_base;
		info += run->style_item;
		if ((font_index = info->font_index) == DEFAULT_FONT_INDEX)
			font_index = 0;

		return  (pg_boolean) ((walker->cur_font->code_page != walker->font_base[font_index].code_page)
				|| (walker->cur_font->machine_var[PG_CHARSET] != walker->font_base[font_index].machine_var[PG_CHARSET])
				|| (walker->cur_font->language != walker->font_base[font_index].language) );
	}

	return  FALSE;
}


/* set_non_roman_bits sets all the appropriate bits for the character in question. For performance,
if known_leadbyte is TRUE then we do not need to go backwards to compute the bytecount per char.
The ctype_3 param gets set to the result of CTYPE_3 query. */

static long set_non_roman_bits (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
		long offset_begin, long offset_end, long char_offset, long mask_bits,
		WORD PG_FAR *ctype_3, pg_short_t PG_FAR *symbol_test, pg_boolean known_leadbyte)
{
	LCID			lcid;
	WORD			type3, types[4];
	pg_short_t		symbol_check = 0;
	long			result = 0;

#ifdef UNICODE
	
	symbol_check = data[char_offset];

	lcid = (LCID)walker->cur_font->language;

	if (mask_bits & (LONG_FORM_INFO | BLANK_BIT)) {

		GetStringTypeEx(lcid, CT_CTYPE1, (PGSTR)&data[char_offset], 1, types);

		if (types[0] & C1_BLANK)
			result |= BLANK_BIT;

		if (mask_bits & LONG_FORM_INFO) {

			if (types[0] & C1_PUNCT)
				result |= PUNCT_NORMAL_BIT;
			if (types[0] & C1_UPPER)
				result |= UPPER_CASE_BIT;
			if (types[0] & C1_LOWER)
				result |= LOWER_CASE_BIT;
			if (types[0] & C1_DIGIT)
				result |= NUMBER_BIT;
		}
	}
	
	if (ctype_3)
		type3 = pg->procs.charclass_proc(pg, &data[char_offset], 1,
				walker->cur_style, walker->cur_font);

	if (symbol_test)
		*symbol_test = symbol_check;
	if (ctype_3)
		*ctype_3 = type3;

	return		result;
#else
	UINT			code_page;

   	code_page = walker->cur_font->code_page;		/* get font codepage */
	result |= multibyte_character_flags(code_page, data, offset_begin, char_offset, known_leadbyte);
	
	if (symbol_test)
		symbol_check = *symbol_test;
	
	type3 = 0;

	if ((result & (FIRST_HALF_BIT | LAST_HALF_BIT)) != 0) {
		long				first_byte;
		int					byte_count;
		pg_short_t			half_char;
		
		lcid = (LCID)walker->cur_font->language;
		first_byte = char_offset;
		byte_count = 1;

		if (result & LAST_HALF_BIT) {
			
			if (offset_begin < char_offset) {
				
				first_byte = char_offset - 1;
				byte_count += 1;
				half_char = (pg_short_t)data[first_byte];
				half_char <<= 8;
				symbol_check |= half_char;
			}
			else
				symbol_check = 0;
		}
		else {
			
			if (offset_end > (char_offset + 1)) {
			
				byte_count += 1;
				symbol_check <<= 8;
				symbol_check |= (pg_short_t)data[char_offset];
			}
		}
		
		if (mask_bits & (LONG_FORM_INFO | BLANK_BIT)) {

			GetStringTypeEx(lcid, CT_CTYPE1, (LPCSTR)&data[first_byte], byte_count, types);

			if (types[0] & C1_BLANK)
				result |= BLANK_BIT;

			if (mask_bits & LONG_FORM_INFO) {

				if (types[0] & C1_PUNCT)
					result |= PUNCT_NORMAL_BIT;
				if (types[0] & C1_UPPER)
					result |= UPPER_CASE_BIT;
				if (types[0] & C1_LOWER)
					result |= LOWER_CASE_BIT;
				if (types[0] & C1_DIGIT)
					result |= NUMBER_BIT;
			}
		}
		
		if (ctype_3)
			type3 = pg->procs.charclass_proc(pg, &data[first_byte], (short)byte_count,
					walker->cur_style, walker->cur_font);
	}

	if (symbol_test)
		*symbol_test = symbol_check;
	if (ctype_3)
		*ctype_3 = type3;

	return	result;
#endif
}



/* Initializes Paige font info in a WIndows environment */

static void init_font_record (paige_rec_ptr pg, font_info_ptr font)
{

	HDC		     			hdc;
	pg_globals_ptr			globals;
	style_info				the_style;
	HANDLE					previous_font;
	short					char_type;

	globals = pg->globals;
	
	the_style = globals->def_style;
	the_style.procs.init(pg, &the_style, font);
	
	get_windows_font_object(pg, &the_style, font);

	hdc = pgGetPlatformDevice((graf_device_ptr)&pg->port);
	previous_font = SelectObject(hdc, (HANDLE) the_style.machine_var);
	
#ifdef WIN_MULTILINGUAL	
#ifdef WIN16_COMPILE
	font->code_page = GetKBCodePage();
	
	//Emulate GetTextCharset
	{
		font_object_ptr					fonts;
	
		fonts = UseMemoryRecord(globals->font_objects, the_style.machine_var2, 0, TRUE);
		font->char_type = (short)fonts->log.lfCharSet & SCRIPT_CODE_MASK;
		UnuseMemory(globals->font_objects);	
	}
#else
	font->code_page = GetACP();
	font->char_type = GetTextCharset(hdc) & SCRIPT_CODE_MASK;
#endif
	init_font_language(font, hdc);
#else
	font->char_type = WIN_CHARSET;		//Win16 character set
	font->code_page = WIN_CODEPAGE;		//Win16 code page
	font->language = WIN_LANGUAGE;		//Win16 language
#endif

	if (font->machine_var[PG_CHARSET] > SYMBOL_CHARSET)
		font->char_type |= NON_ROMAN_FLAG;

	char_type = (short)font->machine_var[PG_CHARSET];

	if (char_type == HEBREW_CHARSET || char_type == ARABIC_CHARSET || char_type == THAI_CHARSET)
		font->char_type |= RIGHT_FONT_FLAG;
	
	SelectObject(hdc, previous_font);
	pgReleasePlatformDevice((graf_device_ptr)&pg->port);
}


/* Converts Windows language id to numeric - Can't find any other way to do this.
Fixed up by Gar 1/11/96 to create a valid LCID and make machine_var[PG_LANGINFO]
contain actual language and script info. */

static void init_font_language (font_info_ptr font, HDC hdc)
{

#ifndef WIN_MULTILINGUAL
	font->machine_var[PG_LANGINFO] = font->language = 0;
#else

	font->language = (long)(MAKELCID((MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)), SORT_DEFAULT));
	
	if (((font->platform & GENERAL_PLATFORM_MASK) != GENERAL_WINDOWS) &&
		(font->environs & FONT_NOT_AVAIL))
		font->machine_var[PG_LANGINFO] = 0;
	else
		font->machine_var[PG_LANGINFO] = (long)(x_GetFontLanguageInfo(hdc));

#endif
}

/* multibyte_character_flags returns the PAIGE character bits for first half, last half
attributes given the code page, string and offset in question. If known_leadbyte is TRUE then
we are calling this knowing that the first byte in question is the first byte of the char. */

static long multibyte_character_flags (UINT code_page, pg_char_ptr string, long offset_begin,
			long offset_end, pg_boolean known_leadbyte)
{
#ifdef UNICODE
	return		0;
#else 

#ifndef WIN_MULTILINGUAL
	return		0;
#else
	long			result = 0;
	register long	index;
	BOOL			previous_leadflag = FALSE;
	
	index = offset_end;
	
	if (!known_leadbyte && (index > offset_begin) ) {

			// Compute the true lead byte flag of previous byte

		while (index > offset_begin) {
		
			--index;

			if (!FirstMultiCharByte(code_page, string[index])) {
				
				++index;
				break;
			}
		}
		
		while (index < offset_end) {
			
			previous_leadflag = FALSE;

			if (FirstMultiCharByte(code_page, string[index])) {

				index += 2;

				if (index > offset_end)
					previous_leadflag = TRUE;
			}
			else
				index += 1;
		}
		
		index = offset_end;
	}

	if (FirstMultiCharByte(code_page, string[index])) {
		
		if (!previous_leadflag)
			result |= FIRST_HALF_BIT;
		else
			result |= LAST_HALF_BIT;
	}
	else {
		
		if (previous_leadflag)
			result |= LAST_HALF_BIT;
	}
	return	result;
#endif
#endif
}

/* Blink the style */

static void draw_box_blink (paige_rec_ptr pg, style_walk_ptr walker,
		draw_points_ptr draw_position)
{
	rectangle						box_frame;
	Rect							frame;
	point_start_ptr					line_start;
	long							line_width, descent, next_offset;
	HDC							    hdc;
	
	if (blink_mode == 1)
	{
		line_start = draw_position->starts;
		next_offset = line_start->offset;
		next_offset += draw_position->block->begin;
		pgSetWalkStyle(walker, next_offset);
	
		descent = pgGetLineRealDescent(draw_position->block, line_start, walker);
		pgScaleLong(pg->scale_factor.scale, 0, &descent);
	
		box_frame.top_left = box_frame.bot_right = draw_position->from;
		box_frame.top_left.v -= draw_position->ascent;
		box_frame.bot_right.v += descent;
		line_width = line_start->bounds.bot_right.h - line_start->bounds.top_left.h;
		pgScaleLong(pg->scale_factor.scale, 0, &line_width);
		box_frame.bot_right.h = box_frame.top_left.h + line_width;
		
		RectangleToRect(&box_frame, NULL, &frame);
			
		hdc = pgGetPlatformDevice(&pg->port);
		InvertRect(hdc, &frame);
		pgReleasePlatformDevice(&pg->port);
	}
}


#ifdef WIN16_COMPILE
// okay. All we want to know is if the font has DBCS.
// In the MSDN October CD, there was an article that stated
// how incredibly difficult this was. Let's just try to fake it
// out with the code page numbers.

static short fe_code_pages[] =
{
	932, 936, 949, 950, 874, 864, 862, 708, 1256, 1255, 0
};

static
void
GetCPInfo(short code_page, CPINFO *cp_info)
{
	int i = 0;
	while (fe_code_pages[i])
	{
		if (fe_code_pages[i] == code_page)
			break;
		i++;
	}
	if (fe_code_pages[i])
		cp_info->MaxCharSize = 2;
}

static
void
GetStringTypeEx(LCID loc,DWORD itype,LPCSTR str,int csx,LPWORD rtype)
{
#ifdef WIN_MULTILINGUAL
	GetStringTypeA(loc,itype,str,csx,rtype);
#endif
}

#endif

#ifdef WIN16_COMPILE
#ifdef WIN_MULTILINGUAL
DWORD WINAPI 
x_GetFontLanguageInfo( HDC dc )
{
	TEXTMETRIC tm;
	LANGID lang,sublang,sort;
	LCID result;

	GetTextMetrics(dc,&tm);
	sublang = SUBLANG_NEUTRAL;
	sort = SORT_DEFAULT;
	switch (tm.tmCharSet)
	{
		case CHINESEBIG5_CHARSET:
			lang = LANG_CHINESE;
			sort = SORT_CHINESE_BIG5;
			sublang = SUBLANG_CHINESE_TRADITIONAL;
			break;
	/*	case GB2312_CHARSET:
			lang = LANG_CHINESE;
			sublang = SUBLANG_CHINESE_SIMPLIFIED;
			break;    */
			
		case HANGEUL_CHARSET:
			lang = LANG_KOREAN;
			sort = SORT_KOREAN_KSC;
			sublang = SUBLANG_KOREAN;
			break;
		case SHIFTJIS_CHARSET:
			lang = LANG_JAPANESE;
			sort = SORT_JAPANESE_XJIS;
			break;
		default:
			result = GetUserDefaultLCID();
			if (!result)
				result = GetSystemDefaultLCID();
			if (result)
				return result;
			lang = LANG_ENGLISH;
			sublang = SUBLANG_ENGLISH_US;
			break;
	}
	result = MAKELCID(MAKELANGID(lang,sublang),sort);
	return (result);
}
#endif
#endif
#endif
