/*
* tkWinGDI.c --
*
* This module implements access to the Win32 GDI API.
*
* Copyright © 1991-2018 Microsoft Corp.
* Copyright © 2009, Michael I. Schwartz.
* Copyright © 1998-2019 Harald Oehlmann, Elmicron GmbH
* Copyright © 2021 Kevin Walzer/WordTech Communications LLC.
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include <windows.h>
#include <math.h>
#include <wtypes.h>
#include <winspool.h>
#include <commdlg.h>
#include <wingdi.h>
#include <tcl.h>
#include "tkWinInt.h"
/*
* Create a standard "DrawFunc" to make this more workable....
*/
#ifdef _MSC_VER
typedef BOOL (WINAPI *DrawFunc) (
HDC, int, int, int, int, int, int, int, int); /* Arc, Chord, Pie. */
#else
typedef BOOL WINAPI (*DrawFunc) (
HDC, int, int, int, int, int, int, int, int); /* Arc, Chord, Pie. */
#endif
/* Real functions. */
static Tcl_ObjCmdProc2 GdiArc;
static Tcl_ObjCmdProc2 GdiBitmap;
static Tcl_ObjCmdProc2 GdiCharWidths;
static Tcl_ObjCmdProc2 GdiImage;
static Tcl_ObjCmdProc2 GdiPhoto;
static Tcl_ObjCmdProc2 GdiLine;
static Tcl_ObjCmdProc2 GdiOval;
static Tcl_ObjCmdProc2 GdiPolygon;
static Tcl_ObjCmdProc2 GdiRectangle;
static Tcl_ObjCmdProc2 GdiText;
static Tcl_ObjCmdProc2 GdiMap;
static Tcl_ObjCmdProc2 GdiCopyBits;
/* Local copies of similar routines elsewhere in Tcl/Tk. */
static int GdiGetColor(Tcl_Obj *nameObj, COLORREF *color);
/*
* Helper functions.
*/
static int GdiMakeLogFont(Tcl_Interp *interp, const char *str,
LOGFONTW *lf, HDC hDC);
static int GdiMakePen(Tcl_Interp *interp, double dwidth,
int dashstyle, const char *dashstyledata,
int capstyle, int joinstyle,
int stipplestyle, const char *stippledata,
unsigned long color, HDC hDC, HGDIOBJ *oldPen);
static int GdiFreePen(Tcl_Interp *interp, HDC hDC, HGDIOBJ oldPen);
static int GdiMakeBrush(unsigned long color, long hatch,
LOGBRUSH *lb, HDC hDC, HBRUSH *oldBrush);
static void GdiFreeBrush(Tcl_Interp *interp, HDC hDC,
HGDIOBJ oldBrush);
static int GdiGetHdcInfo(HDC hdc,
LPPOINT worigin, LPSIZE wextent,
LPPOINT vorigin, LPSIZE vextent);
/* Helper functions for printing the window client area. */
enum PrintType { PTWindow = 0, PTClient = 1, PTScreen = 2 };
static HANDLE CopyToDIB(HWND wnd, enum PrintType type);
static HBITMAP CopyScreenToBitmap(LPRECT lpRect);
static HANDLE BitmapToDIB(HBITMAP hb, HPALETTE hp);
static HANDLE CopyScreenToDIB(LPRECT lpRect);
static int DIBNumColors(LPBITMAPINFOHEADER lpDIB);
static int PalEntriesOnDevice(HDC hDC);
static HPALETTE GetSystemPalette(void);
static void GetDisplaySize(LONG *width, LONG *height);
static int GdiWordToWeight(const char *str);
static int GdiParseFontWords(Tcl_Interp *interp, LOGFONTW *lf,
const char *str[], int numargs);
static Tcl_ObjCmdProc2 PrintSelectPrinter;
static Tcl_ObjCmdProc2 PrintOpenPrinter;
static Tcl_ObjCmdProc2 PrintClosePrinter;
static Tcl_ObjCmdProc2 PrintOpenDoc;
static Tcl_ObjCmdProc2 PrintCloseDoc;
static Tcl_ObjCmdProc2 PrintOpenPage;
static Tcl_ObjCmdProc2 PrintClosePage;
/*
* Global state.
*/
static PRINTDLGW pd;
static DOCINFOW di;
static WCHAR *localPrinterName = NULL;
static int copies, paper_width, paper_height, dpi_x, dpi_y;
static LPDEVNAMES devnames;
static HDC printDC;
/*
* To make the "subcommands" follow a standard convention, add them to this
* array. The first element is the subcommand name, and the second a standard
* Tcl command handler.
*/
static const struct gdi_command {
const char *command_string;
Tcl_ObjCmdProc2 *command;
} gdi_commands[] = {
{ "arc", GdiArc },
{ "bitmap", GdiBitmap },
{ "characters", GdiCharWidths },
{ "image", GdiImage },
{ "line", GdiLine },
{ "map", GdiMap },
{ "oval", GdiOval },
{ "photo", GdiPhoto },
{ "polygon", GdiPolygon },
{ "rectangle", GdiRectangle },
{ "text", GdiText },
{ "copybits", GdiCopyBits },
};
/*
*----------------------------------------------------------------------
*
* GdiArc --
*
* Map canvas arcs to GDI context.
*
* Results:
* Renders arcs.
*
*----------------------------------------------------------------------
*/
static int GdiArc(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi arc hdc x1 y1 x2 y2 "
"-extent angle -start angle -style arcstyle "
"-fill color -outline color "
"-width dimension -dash dashrule "
"-outlinestipple ignored -stipple ignored\n" ;
double x1, y1, x2, y2;
int xr0, yr0, xr1, yr1;
HDC hDC;
double extent = 0.0, start = 0.0;
DrawFunc drawfunc;
double width = 0.0;
HPEN hPen;
COLORREF linecolor = 0, fillcolor = BS_NULL;
int dolinecolor = 0, dofillcolor = 0;
HBRUSH hBrush = NULL;
LOGBRUSH lbrush;
HGDIOBJ oldobj = NULL;
int dodash = 0;
const char *dashdata = 0;
drawfunc = Pie;
/* Verrrrrry simple for now.... */
if (objc < 6) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
if ((Tcl_GetDoubleFromObj(interp, objv[2], &x1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &y1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[4], &x2) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &y2) != TCL_OK)) {
return TCL_ERROR;
}
objc -= 6;
objv += 6;
while (objc >= 2) {
if (strcmp(Tcl_GetString(objv[0]), "-extent") == 0) {
extent = atof(Tcl_GetString(objv[1]));
} else if (strcmp(Tcl_GetString(objv[0]), "-start") == 0) {
start = atof(Tcl_GetString(objv[1]));
} else if (strcmp(Tcl_GetString(objv[0]), "-style") == 0) {
if (strcmp(Tcl_GetString(objv[1]), "pieslice") == 0) {
drawfunc = Pie;
} else if (strcmp(Tcl_GetString(objv[1]), "arc") == 0) {
drawfunc = Arc;
} else if (strcmp(Tcl_GetString(objv[1]), "chord") == 0) {
drawfunc = Chord;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-fill") == 0) {
/* Handle all args, even if we don't use them yet. */
if (GdiGetColor(objv[1], &fillcolor)) {
dofillcolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-outline") == 0) {
if (GdiGetColor(objv[1], &linecolor)) {
dolinecolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-outlinestipple") == 0) {
/* ignored */
} else if (strcmp(Tcl_GetString(objv[0]), "-stipple") == 0) {
/* ignored */
} else if (strcmp(Tcl_GetString(objv[0]), "-width") == 0) {
if (Tcl_GetDoubleFromObj(interp, objv[1], &width)) {
return TCL_ERROR;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-dash") == 0) {
if (Tcl_GetString(objv[1])) {
dodash = 1;
dashdata = Tcl_GetString(objv[1]);
}
} else {
/* Don't know that option! */
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
objc -= 2;
objv += 2;
}
xr0 = xr1 = (x1 + x2) / 2;
yr0 = yr1 = (y1 + y2) / 2;
/*
* The angle used by the arc must be "warped" by the eccentricity of the
* ellipse. Thanks to Nigel Dodd <[email protected]> for bringing a
* nice example.
*/
xr0 += (int)(100.0 * (x2 - x1) * cos((start * 2.0 * 3.14159265) / 360.0));
yr0 -= (int)(100.0 * (y2 - y1) * sin((start * 2.0 * 3.14159265) / 360.0));
xr1 += (int)(100.0 * (x2 - x1) * cos(((start+extent) * 2.0 * 3.14159265) / 360.0));
yr1 -= (int)(100.0 * (y2 - y1) * sin(((start+extent) * 2.0 * 3.14159265) / 360.0));
/*
* Under Win95, SetArcDirection isn't implemented--so we have to assume
* that arcs are drawn counterclockwise (e.g., positive extent) So if it's
* negative, switch the coordinates!
*/
if (extent < 0) {
int xr2 = xr0;
int yr2 = yr0;
xr0 = xr1;
xr1 = xr2;
yr0 = yr1;
yr1 = yr2;
}
if (dofillcolor) {
GdiMakeBrush(fillcolor, 0, &lbrush, hDC, &hBrush);
} else {
oldobj = SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
}
if (width || dolinecolor) {
GdiMakePen(interp, width, dodash, dashdata,
0, 0, 0, 0, linecolor, hDC, (HGDIOBJ *)&hPen);
}
(*drawfunc)(hDC, x1, y1, x2, y2, xr0, yr0, xr1, yr1);
if (width || dolinecolor) {
GdiFreePen(interp, hDC, hPen);
}
if (hBrush) {
GdiFreeBrush(interp, hDC, hBrush);
} else {
SelectObject(hDC, oldobj);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiBitmap --
*
* Unimplemented for now. Should use the same techniques as
* CanvasPsBitmap (tkCanvPs.c).
*
* Results:
* None.
*
*----------------------------------------------------------------------
*/
static int GdiBitmap(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
Tcl_Obj *const *objv)
{
/*
* Skip this for now. Should be based on common code with the copybits
* command.
*/
Tcl_WrongNumArgs(interp, 1, objv, "hdc x y "
"-anchor [center|n|e|s|w] -background color "
"-bitmap bitmap -foreground color\n"
"Not implemented yet. Sorry!");
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* GdiImage --
*
* Unimplemented for now. Unimplemented for now. Should switch on image
* type and call either GdiPhoto or GdiBitmap. This code is similar to
* that in tkWinImage.c.
*
* Results:
* None.
*
*----------------------------------------------------------------------
*/
static int GdiImage(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
Tcl_Obj *const *objv)
{
/* Skip this for now..... */
/* Should be based on common code with the copybits command. */
Tcl_WrongNumArgs(interp, 1, objv, "hdc x y -anchor [center|n|e|s|w] -image name\n"
"Not implemented yet. Sorry!");
/* Normally, usage results in TCL_ERROR--but wait til' it's implemented. */
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiPhoto --
*
* Contributed by Lukas Rosenthaler <[email protected]>
*
* Note: The canvas doesn't directly support photos (only as images), so
* this is the first ::tk::print::_gdi command without an equivalent
* canvas command. This code may be modified to support photo images on
* the canvas.
*
* Results:
* Renders a photo.
*
*----------------------------------------------------------------------
*/
static int GdiPhoto(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi photo hdc [-destination x y [w [h]]] -photo name\n";
HDC dst;
int dst_x = 0, dst_y = 0, dst_w = 0, dst_h = 0;
int nx, ny, sll;
const char *photoname = 0; /* For some reason Tk_FindPhoto takes a char *. */
Tk_PhotoHandle photo_handle;
Tk_PhotoImageBlock img_block;
BITMAPINFO bitmapinfo; /* Since we don't need the bmiColors table,
* there is no need for dynamic allocation. */
int oldmode; /* For saving the old stretch mode. */
POINT pt; /* For saving the brush org. */
char *pbuf = NULL;
int i, j, k;
int retval = TCL_OK;
/*
* Parse the arguments.
*/
/* HDC is required. */
if (objc < 2) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
dst = printDC;
/*
* Next, check to see if 'dst' can support BitBlt.
* If not, raise an error.
*/
if ((GetDeviceCaps(dst, RASTERCAPS) & RC_STRETCHDIB) == 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"::tk::print::_gdi photo not supported on device context (0x%s)",
Tcl_GetString(objv[1])));
return TCL_ERROR;
}
/* Parse the command line arguments. */
for (j = 2; j < objc; j++) {
if (strcmp(Tcl_GetString(objv[j]), "-destination") == 0) {
double x, y, w, h;
int count = 0;
char dummy;
if (j < objc) {
count = sscanf(Tcl_GetString(objv[++j]), "%lf%lf%lf%lf%c",
&x, &y, &w, &h, &dummy);
}
if (count < 2 || count > 4) {
/* Destination must provide at least 2 arguments. */
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"-destination requires a list of 2 to 4 numbers\n%s",
usage_message));
return TCL_ERROR;
}
dst_x = (int) x;
dst_y = (int) y;
if (count == 3) {
dst_w = (int) w;
dst_h = -1;
} else if (count == 4) {
dst_w = (int) w;
dst_h = (int) h;
}
} else if (strcmp(Tcl_GetString(objv[j]), "-photo") == 0) {
photoname = Tcl_GetString(objv[++j]);
}
}
if (photoname == 0) { /* No photo provided. */
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"No photo name provided to ::tk::print::_gdi photo\n%s",
usage_message));
return TCL_ERROR;
}
photo_handle = Tk_FindPhoto(interp, photoname);
if (photo_handle == 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"::tk::print::_gdi photo: Photo name %s can't be located\n%s",
photoname, usage_message));
return TCL_ERROR;
}
Tk_PhotoGetImage(photo_handle, &img_block);
nx = img_block.width;
ny = img_block.height;
sll = ((3*nx + 3) / 4)*4; /* Must be multiple of 4. */
/*
* Buffer is potentially large enough that failure to allocate might be
* recoverable.
*/
pbuf = (char *)attemptckalloc(sll * ny * sizeof(char));
if (pbuf == 0) { /* Memory allocation failure. */
Tcl_AppendResult(interp,
"::tk::print::_gdi photo failed--out of memory", (char *)NULL);
return TCL_ERROR;
}
/* After this, all returns must go through retval. */
/* BITMAP expects BGR; photo provides RGB. */
for (k = 0; k < ny; k++) {
for (i = 0; i < nx; i++) {
pbuf[k*sll + 3*i] = img_block.pixelPtr[
k*img_block.pitch + i*img_block.pixelSize + img_block.offset[2]];
pbuf[k*sll + 3*i + 1] = img_block.pixelPtr[
k*img_block.pitch + i*img_block.pixelSize + img_block.offset[1]];
pbuf[k*sll + 3*i + 2] = img_block.pixelPtr[
k*img_block.pitch + i*img_block.pixelSize + img_block.offset[0]];
}
}
memset(&bitmapinfo, 0L, sizeof(BITMAPINFO));
bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biWidth = nx;
bitmapinfo.bmiHeader.biHeight = -ny;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biBitCount = 24;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
bitmapinfo.bmiHeader.biSizeImage = 0; /* sll*ny;. */
bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
bitmapinfo.bmiHeader.biClrUsed = 0;
bitmapinfo.bmiHeader.biClrImportant = 0;
oldmode = SetStretchBltMode(dst, HALFTONE);
/*
* According to the Win32 Programmer's Manual, we have to set the brush
* org, now.
*/
SetBrushOrgEx(dst, 0, 0, &pt);
if (dst_w <= 0) {
dst_w = nx;
dst_h = ny;
} else if (dst_h <= 0) {
dst_h = ny*dst_w / nx;
}
if (StretchDIBits(dst, dst_x, dst_y, dst_w, dst_h, 0, 0, nx, ny,
pbuf, &bitmapinfo, DIB_RGB_COLORS, SRCCOPY) == (int)GDI_ERROR) {
int errcode = GetLastError();
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"::tk::print::_gdi photo internal failure: "
"StretchDIBits error code %d", errcode));
retval = TCL_ERROR;
}
/* Clean up the hDC. */
if (oldmode != 0) {
SetStretchBltMode(dst, oldmode);
SetBrushOrgEx(dst, pt.x, pt.y, &pt);
}
ckfree(pbuf);
if (retval == TCL_OK) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"%d %d %d %d", dst_x, dst_y, dst_w, dst_h));
}
return retval;
}
/*
*----------------------------------------------------------------------
*
* Bezierize --
*
* Interface to Tk's line smoother, used for lines and pollies.
* Provided by Jasper Taylor <[email protected]>.
*
* Results:
* Smooths lines.
*
*----------------------------------------------------------------------
*/
static int Bezierize(
POINT* polypoints,
int npoly,
int nStep,
POINT** bpointptr)
{
/* First, translate my points into a list of doubles. */
double *inPointList, *outPointList;
int n;
int nbpoints = 0;
POINT* bpoints;
inPointList = (double *)attemptckalloc(2 * sizeof(double) * npoly);
if (inPointList == 0) {
/* TODO: unreachable */
return nbpoints; /* 0. */
}
for (n=0; n<npoly; n++) {
inPointList[2*n] = polypoints[n].x;
inPointList[2*n + 1] = polypoints[n].y;
}
nbpoints = 1 + npoly * nStep; /* this is the upper limit. */
outPointList = (double *)attemptckalloc(2 * sizeof(double) * nbpoints);
if (outPointList == 0) {
/* TODO: unreachable */
ckfree(inPointList);
return 0;
}
nbpoints = TkMakeBezierCurve(NULL, inPointList, npoly, nStep,
NULL, outPointList);
ckfree(inPointList);
bpoints = (POINT *)attemptckalloc(sizeof(POINT)*nbpoints);
if (bpoints == 0) {
/* TODO: unreachable */
ckfree(outPointList);
return 0;
}
for (n=0; n<nbpoints; n++) {
bpoints[n].x = (long)outPointList[2*n];
bpoints[n].y = (long)outPointList[2*n + 1];
}
ckfree(outPointList);
*bpointptr = bpoints;
return nbpoints;
}
/*
*----------------------------------------------------------------------
*
* GdiLine --
*
* Maps lines to GDI context.
*
* Results:
* Renders lines.
*
*----------------------------------------------------------------------
*/
static int GdiLine(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi line hdc x1 y1 ... xn yn "
"-arrow [first|last|both|none] -arrowshape {d1 d2 d3} "
"-dash dashlist "
"-capstyle [butt|projecting|round] -fill color "
"-joinstyle [bevel|miter|round] -smooth [true|false|bezier] "
"-splinesteps number -stipple bitmap -width linewid";
char *strend;
POINT *polypoints;
int npoly;
int x, y;
HDC hDC;
HPEN hPen;
LOGBRUSH lbrush;
HBRUSH hBrush = NULL;
double width = 0.0;
COLORREF linecolor = 0;
int dolinecolor = 0;
int dosmooth = 0;
int doarrow = 0; /* 0=none; 1=end; 2=start; 3=both. */
int arrowshape[3];
int nStep = 12;
int dodash = 0;
const char *dashdata = 0;
double p1x, p1y, p2x, p2y;
arrowshape[0] = 8;
arrowshape[1] = 10;
arrowshape[2] = 3;
/* Verrrrrry simple for now.... */
if (objc < 6) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
polypoints = (POINT *)attemptckalloc((objc - 1) * sizeof(POINT));
if (polypoints == 0) {
Tcl_AppendResult(interp, "Out of memory in GdiLine", (char *)NULL);
return TCL_ERROR;
}
if ((Tcl_GetDoubleFromObj(interp, objv[2], &p1x) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &p1y) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[4], &p2x) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &p2y) != TCL_OK)
) {
return TCL_ERROR;
}
polypoints[0].x = floor(p1x+0.5);
polypoints[0].y = floor(p1y+0.5);
polypoints[1].x = floor(p2x+0.5);
polypoints[1].y = floor(p2y+0.5);
objc -= 6;
objv += 6;
npoly = 2;
while (objc >= 2) {
/* Check for a number. */
x = strtoul(Tcl_GetString(objv[0]), &strend, 0);
if (strend > Tcl_GetString(objv[0])) {
/* One number.... */
y = strtoul(Tcl_GetString(objv[1]), &strend, 0);
if (strend > Tcl_GetString(objv[1])) {
/* TWO numbers!. */
polypoints[npoly].x = x;
polypoints[npoly].y = y;
npoly++;
objc -= 2;
objv += 2;
} else {
/* Only one number... Assume a usage error. */
ckfree(polypoints);
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
} else {
if (strcmp(Tcl_GetString(*objv), "-arrow") == 0) {
if (strcmp(Tcl_GetString(objv[1]), "none") == 0) {
doarrow = 0;
} else if (strcmp(Tcl_GetString(objv[1]), "both") == 0) {
doarrow = 3;
} else if (strcmp(Tcl_GetString(objv[1]), "first") == 0) {
doarrow = 2;
} else if (strcmp(Tcl_GetString(objv[1]), "last") == 0) {
doarrow = 1;
}
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-arrowshape") == 0) {
/* List of 3 numbers--set arrowshape array. */
int a1, a2, a3;
char dummy;
if (sscanf(Tcl_GetString(objv[1]), "%d%d%d%c", &a1, &a2, &a3, &dummy) == 3
&& a1 > 0 && a2 > 0 && a3 > 0) {
arrowshape[0] = a1;
arrowshape[1] = a2;
arrowshape[2] = a3;
}
/* Else the argument was bad. */
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-capstyle") == 0) {
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-fill") == 0) {
if (GdiGetColor(objv[1], &linecolor)) {
dolinecolor = 1;
}
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-joinstyle") == 0) {
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-smooth") == 0) {
/* Argument is true/false or 1/0 or bezier. */
if (Tcl_GetString(objv[1])) {
switch (Tcl_GetString(objv[1])[0]) {
case 't': case 'T':
case '1':
case 'b': case 'B': /* bezier. */
dosmooth = 1;
break;
default:
dosmooth = 0;
break;
}
objv += 2;
objc -= 2;
}
} else if (strcmp(Tcl_GetString(*objv), "-splinesteps") == 0) {
if (Tcl_GetIntFromObj(interp, objv[1], &nStep) != TCL_OK) {
return TCL_ERROR;
}
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-dash") == 0) {
if (Tcl_GetString(objv[1])) {
dodash = 1;
dashdata = Tcl_GetString(objv[1]);
}
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-dashoffset") == 0) {
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-stipple") == 0) {
objv += 2;
objc -= 2;
} else if (strcmp(Tcl_GetString(*objv), "-width") == 0) {
if (Tcl_GetDoubleFromObj(interp, objv[1], &width) != TCL_OK) {
return TCL_ERROR;
}
objv += 2;
objc -= 2;
} else { /* It's an unknown argument!. */
objc--;
objv++;
}
/* Check for arguments
* Most of the arguments affect the "Pen"
*/
}
}
if (width || dolinecolor || dodash) {
GdiMakePen(interp, width, dodash, dashdata,
0, 0, 0, 0, linecolor, hDC, (HGDIOBJ *)&hPen);
}
if (doarrow != 0) {
GdiMakeBrush(linecolor, 0, &lbrush, hDC, &hBrush);
}
if (dosmooth) { /* Use PolyBezier. */
int nbpoints;
POINT *bpoints = NULL;
nbpoints = Bezierize(polypoints,npoly,nStep,&bpoints);
if (nbpoints > 0) {
Polyline(hDC, bpoints, nbpoints);
} else {
Polyline(hDC, polypoints, npoly); /* Out of memory? Just draw a regular line. */
}
if (bpoints) {
ckfree(bpoints);
}
} else {
Polyline(hDC, polypoints, npoly);
}
if (dodash && doarrow) { /* Don't use dashed or thick pen for the arrows! */
GdiFreePen(interp, hDC, hPen);
GdiMakePen(interp, width, 0, 0, 0, 0, 0, 0,
linecolor, hDC, (HGDIOBJ *)&hPen);
}
/* Now the arrowheads, if any. */
if (doarrow & 1) {
/* Arrowhead at end = polypoints[npoly-1].x, polypoints[npoly-1].y. */
POINT ahead[6];
double dx, dy, length;
double sinTheta, cosTheta;
double vertX, vertY, temp;
double fracHeight;
fracHeight = 2.0 / arrowshape[2];
ahead[0].x = ahead[5].x = polypoints[npoly-1].x;
ahead[0].y = ahead[5].y = polypoints[npoly-1].y;
dx = ahead[0].x - polypoints[npoly-2].x;
dy = ahead[0].y - polypoints[npoly-2].y;
if ((length = hypot(dx, dy)) == 0) {
sinTheta = cosTheta = 0.0;
} else {
sinTheta = dy / length;
cosTheta = dx / length;
}
vertX = ahead[0].x - arrowshape[0]*cosTheta;
vertY = ahead[0].y - arrowshape[0]*sinTheta;
temp = arrowshape[2]*sinTheta;
ahead[1].x = (long)(ahead[0].x - arrowshape[1]*cosTheta + temp);
ahead[4].x = (long)(ahead[1].x - 2 * temp);
temp = arrowshape[2]*cosTheta;
ahead[1].y = (long)(ahead[0].y - arrowshape[1]*sinTheta - temp);
ahead[4].y = (long)(ahead[1].y + 2 * temp);
ahead[2].x = (long)(ahead[1].x*fracHeight + vertX*(1.0-fracHeight));
ahead[2].y = (long)(ahead[1].y*fracHeight + vertY*(1.0-fracHeight));
ahead[3].x = (long)(ahead[4].x*fracHeight + vertX*(1.0-fracHeight));
ahead[3].y = (long)(ahead[4].y*fracHeight + vertY*(1.0-fracHeight));
Polygon(hDC, ahead, 6);
}
if (doarrow & 2) {
/* Arrowhead at end = polypoints[0].x, polypoints[0].y. */
POINT ahead[6];
double dx, dy, length;
double sinTheta, cosTheta;
double vertX, vertY, temp;
double fracHeight;
fracHeight = 2.0 / arrowshape[2];
ahead[0].x = ahead[5].x = polypoints[0].x;
ahead[0].y = ahead[5].y = polypoints[0].y;
dx = ahead[0].x - polypoints[1].x;
dy = ahead[0].y - polypoints[1].y;
if ((length = hypot(dx, dy)) == 0) {
sinTheta = cosTheta = 0.0;
} else {
sinTheta = dy / length;
cosTheta = dx / length;
}
vertX = ahead[0].x - arrowshape[0]*cosTheta;
vertY = ahead[0].y - arrowshape[0]*sinTheta;
temp = arrowshape[2]*sinTheta;
ahead[1].x = (long)(ahead[0].x - arrowshape[1]*cosTheta + temp);
ahead[4].x = (long)(ahead[1].x - 2 * temp);
temp = arrowshape[2]*cosTheta;
ahead[1].y = (long)(ahead[0].y - arrowshape[1]*sinTheta - temp);
ahead[4].y = (long)(ahead[1].y + 2 * temp);
ahead[2].x = (long)(ahead[1].x*fracHeight + vertX*(1.0-fracHeight));
ahead[2].y = (long)(ahead[1].y*fracHeight + vertY*(1.0-fracHeight));
ahead[3].x = (long)(ahead[4].x*fracHeight + vertX*(1.0-fracHeight));
ahead[3].y = (long)(ahead[4].y*fracHeight + vertY*(1.0-fracHeight));
Polygon(hDC, ahead, 6);
}
if (width || dolinecolor || dodash) {
GdiFreePen(interp, hDC, hPen);
}
if (hBrush) {
GdiFreeBrush(interp, hDC, hBrush);
}
ckfree(polypoints);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiOval --
*
* Maps ovals to GDI context.
*
* Results:
* Renders ovals.
*
*----------------------------------------------------------------------
*/
static int GdiOval(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi oval hdc x1 y1 x2 y2 -fill color -outline color "
"-stipple bitmap -width linewid";
double x1, y1, x2, y2;
HDC hDC;
HPEN hPen;
double width = 0.0;
COLORREF linecolor = 0, fillcolor = 0;
int dolinecolor = 0, dofillcolor = 0;
HBRUSH hBrush = NULL;
LOGBRUSH lbrush;
HGDIOBJ oldobj = NULL;
int dodash = 0;
const char *dashdata = 0;
/* Verrrrrry simple for now.... */
if (objc < 6) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
if ((Tcl_GetDoubleFromObj(interp, objv[2], &x1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &y1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[4], &x2) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &y2) != TCL_OK)) {
return TCL_ERROR;
}
if (x1 > x2) {
double x3 = x1;
x1 = x2;
x2 = x3;
}
if (y1 > y2) {
double y3 = y1;
y1 = y2;
y2 = y3;
}
objc -= 6;
objv += 6;
while (objc > 0) {
/* Now handle any other arguments that occur. */
if (strcmp(Tcl_GetString(objv[0]), "-fill") == 0) {
if (Tcl_GetString(objv[1]) && GdiGetColor(objv[1], &fillcolor)) {
dofillcolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-outline") == 0) {
if (Tcl_GetString(objv[1]) && GdiGetColor(objv[1], &linecolor)) {
dolinecolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-stipple") == 0) {
/* Not actually implemented */
} else if (strcmp(Tcl_GetString(objv[0]), "-width") == 0) {
if (Tcl_GetString(objv[1])) {
if (Tcl_GetDoubleFromObj(interp, objv[1], &width) != TCL_OK) {
return TCL_ERROR;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-dash") == 0) {
if (Tcl_GetString(objv[1])) {
dodash = 1;
dashdata = Tcl_GetString(objv[1]);
}
}
objv += 2;
objc -= 2;
}
if (dofillcolor) {
GdiMakeBrush(fillcolor, 0, &lbrush, hDC, &hBrush);
} else {
oldobj = SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
}
if (width || dolinecolor) {
GdiMakePen(interp, width, dodash, dashdata,
0, 0, 0, 0, linecolor, hDC, (HGDIOBJ *)&hPen);
}
/*
* Per Win32, Rectangle includes lower and right edges--per Tcl8.3.2 and
* earlier documentation, canvas rectangle does not. Thus, add 1 to right
* and lower bounds to get appropriate behavior.
*/
Ellipse(hDC, floor(x1+0.5), floor(y1+0.5), floor(x2+1.5), floor(y2+1.5));
if (width || dolinecolor) {
GdiFreePen(interp, hDC, hPen);
}
if (hBrush) {
GdiFreeBrush(interp, hDC, hBrush);
} else {
SelectObject(hDC, oldobj);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiPolygon --
*
* Maps polygons to GDI context.
*
* Results:
* Renders polygons.
*
*----------------------------------------------------------------------
*/
static int GdiPolygon(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi polygon hdc x1 y1 ... xn yn "
"-fill color -outline color -smooth [true|false|bezier] "
"-splinesteps number -stipple bitmap -width linewid";
char *strend;
POINT *polypoints;
int npoly;
int dosmooth = 0;
int nStep = 12;
int x, y;
HDC hDC;
HPEN hPen;
double width = 0.0;
COLORREF linecolor = 0, fillcolor = BS_NULL;
int dolinecolor = 0, dofillcolor = 0;
LOGBRUSH lbrush;
HBRUSH hBrush = NULL;
HGDIOBJ oldobj = NULL;
int dodash = 0;
const char *dashdata = 0;
double p1x, p1y, p2x, p2y;
/* Verrrrrry simple for now.... */
if (objc < 6) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
polypoints = (POINT *)attemptckalloc((objc - 1) * sizeof(POINT));
if (polypoints == 0) {
/* TODO: unreachable */
Tcl_AppendResult(interp, "Out of memory in GdiLine", (char *)NULL);
return TCL_ERROR;
}
if ((Tcl_GetDoubleFromObj(interp, objv[2], &p1x) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &p1y) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[4], &p2x) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &p2y) != TCL_OK)) {
return TCL_ERROR;
}
polypoints[0].x = floor(p1x + 0.5);
polypoints[0].y = floor(p1y + 0.5);
polypoints[1].x = floor(p2x + 0.5);
polypoints[1].y = floor(p2y + 0.5);
objc -= 6;
objv += 6;
npoly = 2;
while (objc >= 2) {
/* Check for a number */
x = strtoul(Tcl_GetString(objv[0]), &strend, 0);
if (strend > Tcl_GetString(objv[0])) {
/* One number.... */
y = strtoul(Tcl_GetString(objv[1]), &strend, 0);
if (strend > Tcl_GetString(objv[1])) {
/* TWO numbers!. */
polypoints[npoly].x = x;
polypoints[npoly].y = y;
npoly++;
objc -= 2;
objv += 2;
} else {
/* Only one number... Assume a usage error. */
ckfree(polypoints);
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
} else {
/*
* Check for arguments.
* Most of the arguments affect the "Pen" and "Brush".
*/
if (strcmp(Tcl_GetString(objv[0]), "-fill") == 0) {
if (Tcl_GetString(objv[1]) && GdiGetColor(objv[1], &fillcolor)) {
dofillcolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-outline") == 0) {
if (GdiGetColor(objv[1], &linecolor)) {
dolinecolor = 0;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-smooth") == 0) {
if (Tcl_GetString(objv[1])) {
switch (Tcl_GetString(objv[1])[0]) {
case 't': case 'T':
case '1':
case 'b': case 'B': /* bezier. */
dosmooth = 1;
break;
default:
dosmooth = 0;
break;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-splinesteps") == 0) {
if (Tcl_GetString(objv[1])) {
if (Tcl_GetIntFromObj(interp, objv[1], &nStep) != TCL_OK) {
return TCL_ERROR;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-stipple") == 0) {
/* Not supported */
} else if (strcmp(Tcl_GetString(objv[0]), "-width") == 0) {
if (Tcl_GetString(objv[1])) {
if (Tcl_GetDoubleFromObj(interp, objv[1], &width) != TCL_OK) {
return TCL_ERROR;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-dash") == 0) {
if (Tcl_GetString(objv[1])) {
dodash = 1;
dashdata = Tcl_GetString(objv[1]);
}
}
objc -= 2;
objv += 2;
}
}
if (dofillcolor) {
GdiMakeBrush(fillcolor, 0, &lbrush, hDC, &hBrush);
} else {
oldobj = SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
}
if (width || dolinecolor) {
GdiMakePen(interp, width, dodash, dashdata, 0, 0, 0, 0,
linecolor, hDC, (HGDIOBJ *)&hPen);
}
if (dosmooth) {
int nbpoints;
POINT *bpoints = NULL;
nbpoints = Bezierize(polypoints, npoly, nStep, &bpoints);
if (nbpoints > 0) {
Polygon(hDC, bpoints, nbpoints);
} else {
Polygon(hDC, polypoints, npoly);
}
if (bpoints) {
ckfree(bpoints);
}
} else {
Polygon(hDC, polypoints, npoly);
}
if (width || dolinecolor) {
GdiFreePen(interp, hDC, hPen);
}
if (hBrush) {
GdiFreeBrush(interp, hDC, hBrush);
} else {
SelectObject(hDC, oldobj);
}
ckfree(polypoints);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiRectangle --
*
* Maps rectangles to GDI context.
*
* Results:
* Renders rectangles.
*
*----------------------------------------------------------------------
*/
static int GdiRectangle(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi rectangle hdc x1 y1 x2 y2 "
"-fill color -outline color "
"-stipple bitmap -width linewid";
double x1, y1, x2, y2;
HDC hDC;
HPEN hPen;
double width = 0.0;
COLORREF linecolor = 0, fillcolor = BS_NULL;
int dolinecolor = 0, dofillcolor = 0;
LOGBRUSH lbrush;
HBRUSH hBrush = NULL;
HGDIOBJ oldobj = NULL;
int dodash = 0;
const char *dashdata = 0;
/* Verrrrrry simple for now.... */
if (objc < 6) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
if ((Tcl_GetDoubleFromObj(interp, objv[2], &x1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &y1) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[4], &x2) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &y2) != TCL_OK)) {
return TCL_ERROR;
}
if (x1 > x2) {
double x3 = x1;
x1 = x2;
x2 = x3;
}
if (y1 > y2) {
double y3 = y1;
y1 = y2;
y2 = y3;
}
objc -= 6;
objv += 6;
/* Now handle any other arguments that occur. */
while (objc > 1) {
if (strcmp(Tcl_GetString(objv[0]), "-fill") == 0) {
if (Tcl_GetString(objv[1]) && GdiGetColor(objv[1], &fillcolor)) {
dofillcolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-outline") == 0) {
if (Tcl_GetString(objv[1]) && GdiGetColor(objv[1], &linecolor)) {
dolinecolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-stipple") == 0) {
/* Not supported; ignored */
} else if (strcmp(Tcl_GetString(objv[0]), "-width") == 0) {
if (Tcl_GetString(objv[1])) {
if (Tcl_GetDoubleFromObj(interp, objv[1], &width) != TCL_OK) {
return TCL_ERROR;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-dash") == 0) {
if (Tcl_GetString(objv[1])) {
dodash = 1;
dashdata = Tcl_GetString(objv[1]);
}
}
objc -= 2;
objv += 2;
}
/*
* Note: If any fill is specified, the function must create a brush and
* put the coordinates in a RECTANGLE structure, and call FillRect.
* FillRect requires a BRUSH / color.
* If not, the function Rectangle must be called.
*/
if (dofillcolor) {
GdiMakeBrush(fillcolor, 0, &lbrush, hDC, &hBrush);
} else {
oldobj = SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
}
if (width || dolinecolor) {
GdiMakePen(interp, width, dodash, dashdata,
0, 0, 0, 0, linecolor, hDC, (HGDIOBJ *)&hPen);
}
/*
* Per Win32, Rectangle includes lower and right edges--per Tcl8.3.2 and
* earlier documentation, canvas rectangle does not. Thus, add 1 to
* right and lower bounds to get appropriate behavior.
*/
Rectangle(hDC, floor(x1+0.5), floor(y1+0.5), floor(x2+1.5), floor(y2+1.5));
if (width || dolinecolor) {
GdiFreePen(interp, hDC, hPen);
}
if (hBrush) {
GdiFreeBrush(interp, hDC, hBrush);
} else {
SelectObject(hDC, oldobj);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiCharWidths --
*
* Computes /character widths. This is completely inadequate for
* typesetting, but should work for simple text manipulation.
*
* Results:
* Returns character width.
*
*----------------------------------------------------------------------
*/
static int GdiCharWidths(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi characters hdc [-font fontname] [-array ary]";
/*
* Returns widths of characters from font in an associative array.
* Font is currently selected font for HDC if not specified.
* Array name is GdiCharWidths if not specified.
* Widths should be in the same measures as all other values (1/1000 inch).
*/
HDC hDC;
LOGFONTW lf;
HFONT hfont;
HGDIOBJ oldfont;
int made_font = 0;
const char *aryvarname = "GdiCharWidths";
/* For now, assume 256 characters in the font.... */
int widths[256];
int retval;
if (objc < 2) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hDC = printDC;
objc -= 2;
objv += 2;
while (objc > 0) {
if (strcmp(Tcl_GetString(objv[0]), "-font") == 0) {
objc--;
objv++;
if (GdiMakeLogFont(interp, Tcl_GetString(objv[0]), &lf, hDC)) {
if ((hfont = CreateFontIndirectW(&lf)) != NULL) {
made_font = 1;
oldfont = SelectObject(hDC, hfont);
}
}
/* Else leave the font alone!. */
} else if (strcmp(Tcl_GetString(objv[0]), "-array") == 0) {
objv++;
objc--;
if (objc > 0) {
aryvarname = Tcl_GetString(objv[0]);
}
}
objv++;
objc--;
}
/* Now, get the widths using the correct function for font type. */
if ((retval = GetCharWidth32W(hDC, 0, 255, widths)) == FALSE) {
retval = GetCharWidthW(hDC, 0, 255, widths);
}
/*
* Retval should be 1 (TRUE) if the function succeeded. If the function
* fails, get the "extended" error code and return. Be sure to deallocate
* the font if necessary.
*/
if (retval == FALSE) {
DWORD val = GetLastError();
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"::tk::print::_gdi character failed with code %ld", val));
if (made_font) {
SelectObject(hDC, oldfont);
DeleteObject(hfont);
}
return TCL_ERROR;
}
{
int i;
char ind[2];
ind[1] = '\0';
for (i = 0; i < 255; i++) {
/* TODO: use a bytearray for the index name so NUL works */
ind[0] = i;
Tcl_SetVar2Ex(interp, aryvarname, ind, Tcl_NewIntObj(widths[i]),
TCL_GLOBAL_ONLY);
}
}
/* Now, remove the font if we created it only for this function. */
if (made_font) {
SelectObject(hDC, oldfont);
DeleteObject(hfont);
}
/* The return value should be the array name(?). */
Tcl_AppendResult(interp, aryvarname, (char *)NULL);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiText --
*
* Maps text to GDI context.
*
* Results:
* Renders text.
*
*----------------------------------------------------------------------
*/
int GdiText(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi text hdc x y -anchor [center|n|e|s|w] "
"-fill color -font fontname "
"-justify [left|right|center] "
"-stipple bitmap -text string -width linelen "
"-single -backfill";
HDC hDC;
double x, y;
const char *string = 0;
RECT sizerect;
UINT format_flags = DT_EXPANDTABS|DT_NOPREFIX; /* Like the canvas. */
Tk_Anchor anchor = TK_ANCHOR_N;
LOGFONTW lf;
HFONT hfont;
HGDIOBJ oldfont;
int made_font = 0;
int retval;
int dotextcolor = 0;
int dobgmode = 0;
int bgmode;
COLORREF textcolor = 0;
int usesingle = 0;
WCHAR *wstring;
Tcl_DString tds;
if (objc < 4) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
/* Parse the command. */
hDC = printDC;
if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK)) {
return TCL_ERROR;
}
objc -= 4;
objv += 4;
sizerect.left = sizerect.right = floor(x+0.5);
sizerect.top = sizerect.bottom = floor(y+0.5);
while (objc > 0) {
if (strcmp(Tcl_GetString(objv[0]), "-anchor") == 0) {
objc--;
objv++;
if (objc > 0) {
Tk_GetAnchor(interp, Tcl_GetString(objv[0]), &anchor);
}
} else if (strcmp(Tcl_GetString(objv[0]), "-justify") == 0) {
objc--;
objv++;
if (objc > 0) {
if (strcmp(Tcl_GetString(objv[0]), "left") == 0) {
format_flags |= DT_LEFT;
} else if (strcmp(Tcl_GetString(objv[0]), "center") == 0) {
format_flags |= DT_CENTER;
} else if (strcmp(Tcl_GetString(objv[0]), "right") == 0) {
format_flags |= DT_RIGHT;
}
}
} else if (strcmp(Tcl_GetString(objv[0]), "-text") == 0) {
objc--;
objv++;
if (objc > 0) {
string = Tcl_GetString(objv[0]);
}
} else if (strcmp(Tcl_GetString(objv[0]), "-font") == 0) {
objc--;
objv++;
if (GdiMakeLogFont(interp, Tcl_GetString(objv[0]), &lf, hDC)) {
if ((hfont = CreateFontIndirectW(&lf)) != NULL) {
made_font = 1;
oldfont = SelectObject(hDC, hfont);
}
}
/* Else leave the font alone! */
} else if (strcmp(Tcl_GetString(objv[0]), "-stipple") == 0) {
objc--;
objv++;
/* Not implemented yet. */
} else if (strcmp(Tcl_GetString(objv[0]), "-fill") == 0) {
objc--;
objv++;
/* Get text color. */
if (GdiGetColor(objv[0], &textcolor)) {
dotextcolor = 1;
}
} else if (strcmp(Tcl_GetString(objv[0]), "-width") == 0) {
objc--;
objv++;
if (objc > 0) {
int value;
if (Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_OK) {
return TCL_ERROR;
}
sizerect.right += value;
}
/* If a width is specified, break at words. */
format_flags |= DT_WORDBREAK;
} else if (strcmp(Tcl_GetString(objv[0]), "-single") == 0) {
usesingle = 1;
} else if (strcmp(Tcl_GetString(objv[0]), "-backfill") == 0) {
dobgmode = 1;
}
objc--;
objv++;
}
if (string == 0) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
/* Set the format flags for -single: Overrides -width. */
if (usesingle == 1) {
format_flags |= DT_SINGLELINE;
format_flags |= DT_NOCLIP;
format_flags &= ~DT_WORDBREAK;
}
Tcl_DStringInit(&tds);
/* Just for fun, let's try translating string to Unicode. */
wstring = Tcl_UtfToWCharDString(string, TCL_INDEX_NONE, &tds);
DrawTextW(hDC, wstring, Tcl_DStringLength(&tds)/2, &sizerect,
format_flags | DT_CALCRECT);
/* Adjust the rectangle according to the anchor. */
x = y = 0;
switch (anchor) {
case TK_ANCHOR_N:
x = (sizerect.right - sizerect.left) / 2;
break;
case TK_ANCHOR_S:
x = (sizerect.right - sizerect.left) / 2;
y = (sizerect.bottom - sizerect.top);
break;
case TK_ANCHOR_E:
x = (sizerect.right - sizerect.left);
y = (sizerect.bottom - sizerect.top) / 2;
break;
case TK_ANCHOR_W:
y = (sizerect.bottom - sizerect.top) / 2;
break;
case TK_ANCHOR_NE:
x = (sizerect.right - sizerect.left);
break;
case TK_ANCHOR_NW:
break;
case TK_ANCHOR_SE:
x = (sizerect.right - sizerect.left);
y = (sizerect.bottom - sizerect.top);
break;
case TK_ANCHOR_SW:
y = (sizerect.bottom - sizerect.top);
break;
default:
x = (sizerect.right - sizerect.left) / 2;
y = (sizerect.bottom - sizerect.top) / 2;
break;
}
sizerect.right -= x;
sizerect.left -= x;
sizerect.top -= y;
sizerect.bottom -= y;
/* Get the color right. */
if (dotextcolor) {
textcolor = SetTextColor(hDC, textcolor);
}
if (dobgmode) {
bgmode = SetBkMode(hDC, OPAQUE);
} else {
bgmode = SetBkMode(hDC, TRANSPARENT);
}
/* Print the text. */
retval = DrawTextW(hDC, wstring,
Tcl_DStringLength(&tds)/2, &sizerect, format_flags);
Tcl_DStringFree(&tds);
/* Get the color set back. */
if (dotextcolor) {
textcolor = SetTextColor(hDC, textcolor);
}
SetBkMode(hDC, bgmode);
if (made_font) {
SelectObject(hDC, oldfont);
DeleteObject(hfont);
}
/* In this case, the return value is the height of the text. */
Tcl_SetObjResult(interp, Tcl_NewIntObj(retval));
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiGetHdcInfo --
*
* Gets salient characteristics of the CTM.
*
* Results:
* The return value is 0 if any failure occurs--in which case none of the
* other values are meaningful. Otherwise the return value is the
* current mapping mode.
*
*----------------------------------------------------------------------
*/
static int GdiGetHdcInfo(
HDC hdc,
LPPOINT worigin,
LPSIZE wextent,
LPPOINT vorigin,
LPSIZE vextent)
{
int mapmode;
int retval;
memset(worigin, 0, sizeof(POINT));
memset(vorigin, 0, sizeof(POINT));
memset(wextent, 0, sizeof(SIZE));
memset(vextent, 0, sizeof(SIZE));
if ((mapmode = GetMapMode(hdc)) == 0) {
/* Failed! */
retval = 0;
} else {
retval = mapmode;
}
if (GetWindowExtEx(hdc, wextent) == FALSE) {
/* Failed! */
retval = 0;
}
if (GetViewportExtEx(hdc, vextent) == FALSE) {
/* Failed! */
retval = 0;
}
if (GetWindowOrgEx(hdc, worigin) == FALSE) {
/* Failed! */
retval = 0;
}
if (GetViewportOrgEx(hdc, vorigin) == FALSE) {
/* Failed! */
retval = 0;
}
return retval;
}
/*
*----------------------------------------------------------------------
*
* GdiNameToMode --
*
* Converts Windows mapping mode names.
*
* Results:
* Mapping modes are delineated.
*
*----------------------------------------------------------------------
*/
static int GdiNameToMode(
const char *name)
{
static const struct gdimodes {
int mode;
const char *name;
} modes[] = {
{ MM_ANISOTROPIC, "MM_ANISOTROPIC" },
{ MM_HIENGLISH, "MM_HIENGLISH" },
{ MM_HIMETRIC, "MM_HIMETRIC" },
{ MM_ISOTROPIC, "MM_ISOTROPIC" },
{ MM_LOENGLISH, "MM_LOENGLISH" },
{ MM_LOMETRIC, "MM_LOMETRIC" },
{ MM_TEXT, "MM_TEXT" },
{ MM_TWIPS, "MM_TWIPS" }
};
size_t i;
for (i=0; i < sizeof(modes) / sizeof(struct gdimodes); i++) {
if (strcmp(modes[i].name, name) == 0) {
return modes[i].mode;
}
}
return atoi(name);
}
/*
*----------------------------------------------------------------------
*
* GdiModeToName --
*
* Converts the mode number to a printable form.
*
* Results:
* Mapping numbers are delineated.
*
*----------------------------------------------------------------------
*/
static const char *GdiModeToName(
int mode)
{
static const struct gdi_modes {
int mode;
const char *name;
} modes[] = {
{ MM_ANISOTROPIC, "Anisotropic" },
{ MM_HIENGLISH, "1/1000 inch" },
{ MM_HIMETRIC, "1/100 mm" },
{ MM_ISOTROPIC, "Isotropic" },
{ MM_LOENGLISH, "1/100 inch" },
{ MM_LOMETRIC, "1/10 mm" },
{ MM_TEXT, "1 to 1" },
{ MM_TWIPS, "1/1440 inch" }
};
size_t i;
for (i=0; i < sizeof(modes) / sizeof(struct gdi_modes); i++) {
if (modes[i].mode == mode) {
return modes[i].name;
}
}
return "Unknown";
}
/*
*----------------------------------------------------------------------
*
* GdiMap --
*
* Sets mapping mode between logical and physical device space.
*
* Results:
* Bridges map modes.
*
*----------------------------------------------------------------------
*/
static int GdiMap(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
static const char usage_message[] =
"::tk::print::_gdi map hdc "
"[-logical x[y]] [-physical x[y]] "
"[-offset {x y} ] [-default] [-mode mode]";
HDC hdc;
int mapmode; /* Mapping mode. */
SIZE wextent; /* Device extent. */
SIZE vextent; /* Viewport extent. */
POINT worigin; /* Device origin. */
POINT vorigin; /* Viewport origin. */
int argno;
/* Keep track of what parts of the function need to be executed. */
int need_usage = 0;
int use_logical = 0;
int use_physical = 0;
int use_offset = 0;
int use_default = 0;
int use_mode = 0;
/* Required parameter: HDC for printer. */
if (objc < 2) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
hdc = printDC;
if ((mapmode = GdiGetHdcInfo(hdc, &worigin, &wextent, &vorigin, &vextent)) == 0) {
/* Failed!. */
Tcl_AppendResult(interp, "Cannot get current HDC info", (char *)NULL);
return TCL_ERROR;
}
/* Parse remaining arguments. */
for (argno = 2; argno < objc; argno++) {
if (strcmp(Tcl_GetString(objv[argno]), "-default") == 0) {
vextent.cx = vextent.cy = wextent.cx = wextent.cy = 1;
vorigin.x = vorigin.y = worigin.x = worigin.y = 0;
mapmode = MM_TEXT;
use_default = 1;
} else if (strcmp(Tcl_GetString(objv[argno]), "-mode") == 0) {
if (argno + 1 >= objc) {
need_usage = 1;
} else {
mapmode = GdiNameToMode(Tcl_GetString(objv[argno + 1]));
use_mode = 1;
argno++;
}
} else if (strcmp(Tcl_GetString(objv[argno]), "-offset") == 0) {
if (argno + 1 >= objc) {
need_usage = 1;
} else {
/* It would be nice if this parsed units as well.... */
if (sscanf(Tcl_GetString(objv[argno + 1]), "%ld%ld",
&vorigin.x, &vorigin.y) == 2) {
use_offset = 1;
} else {
need_usage = 1;
}
argno++;
}
} else if (strcmp(Tcl_GetString(objv[argno]), "-logical") == 0) {
if (argno + 1 >= objc) {
need_usage = 1;
} else {
int count;
argno++;
/* In "real-life", this should parse units as well.. */
if ((count = sscanf(Tcl_GetString(objv[argno]), "%ld%ld",
&wextent.cx, &wextent.cy)) != 2) {
if (count == 1) {
mapmode = MM_ISOTROPIC;
use_logical = 1;
wextent.cy = wextent.cx; /* Make them the same. */
} else {
need_usage = 1;
}
} else {
mapmode = MM_ANISOTROPIC;
use_logical = 2;
}
}
} else if (strcmp(Tcl_GetString(objv[argno]), "-physical") == 0) {
if (argno + 1 >= objc) {
need_usage = 1;
} else {
int count;
argno++;
/* In "real-life", this should parse units as well.. */
if ((count = sscanf(Tcl_GetString(objv[argno]), "%ld%ld",
&vextent.cx, &vextent.cy)) != 2) {
if (count == 1) {
mapmode = MM_ISOTROPIC;
use_physical = 1;
vextent.cy = vextent.cx; /* Make them the same. */
} else {
need_usage = 1;
}
} else {
mapmode = MM_ANISOTROPIC;
use_physical = 2;
}
}
}
}
/* Check for any impossible combinations. */
if (use_logical != use_physical) {
need_usage = 1;
}
if (use_default && (use_logical || use_offset || use_mode)) {
need_usage = 1;
}
if (use_mode && use_logical &&
(mapmode != MM_ISOTROPIC && mapmode != MM_ANISOTROPIC)) {
need_usage = 1;
}
if (need_usage) {
Tcl_AppendResult(interp, usage_message, NULL);
return TCL_ERROR;
}
/* Call Windows CTM functions. */
if (use_logical || use_default || use_mode) { /* Don't call for offset only. */
SetMapMode(hdc, mapmode);
}
if (use_offset || use_default) {
POINT oldorg;
SetViewportOrgEx(hdc, vorigin.x, vorigin.y, &oldorg);
SetWindowOrgEx(hdc, worigin.x, worigin.y, &oldorg);
}
if (use_logical) { /* Same as use_physical. */
SIZE oldsiz;
SetWindowExtEx(hdc, wextent.cx, wextent.cy, &oldsiz);
SetViewportExtEx(hdc, vextent.cx, vextent.cy, &oldsiz);
}
/*
* Since we may not have set up every parameter, get them again for the
* report.
*/
mapmode = GdiGetHdcInfo(hdc, &worigin, &wextent, &vorigin, &vextent);
/*
* Output current CTM info.
* Note: This should really be in terms that can be used in a
* ::tk::print::_gdi map command!
*/
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"Transform: \"(%ld, %ld) -> (%ld, %ld)\" "
"Origin: \"(%ld, %ld)\" "
"MappingMode: \"%s\"",
vextent.cx, vextent.cy, wextent.cx, wextent.cy,
vorigin.x, vorigin.y,
GdiModeToName(mapmode)));
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* GdiCopyBits --
*
* Copies window bits from source to destination.
*
* Results:
* Copies window bits.
*
*----------------------------------------------------------------------
*/
static int GdiCopyBits(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const *objv)
{
/* Goal: get the Tk_Window from the top-level
* convert it to an HWND
* get the HDC
* Do a bitblt to the given hdc
* Use an optional parameter to point to an arbitrary window instead of
* the main
* Use optional parameters to map to the width and height required for the
* dest.
*/
static const char usage_message[] =
"::tk::print::_gdi copybits hdc [-window w|-screen] [-client] "
"[-source \"a b c d\"] "
"[-destination \"a b c d\"] [-scale number] [-calc]";
Tk_Window mainWin;
Tk_Window workwin;
Window wnd;
HDC src;
HDC dst;
HWND hwnd = 0;
HANDLE hDib; /* Handle for device-independent bitmap. */
LPBITMAPINFOHEADER lpDIBHdr;
LPSTR lpBits;
enum PrintType wintype = PTWindow;
int hgt, wid;
char *strend;
long errcode;
Tcl_Size k;
/* Variables to remember what we saw in the arguments. */
int do_window = 0;
int do_screen = 0;
int do_scale = 0;
int do_print = 1;
/* Variables to remember the values in the arguments. */
const char *window_spec;
double scale = 1.0;
int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
int dst_x = 0, dst_y = 0, dst_w = 0, dst_h = 0;
int is_toplevel = 0;
/*
* The following steps are peculiar to the top level window.
* There is likely a clever way to do the mapping of a widget pathname to
* the proper window, to support the idea of using a parameter for this
* purpose.
*/
if ((workwin = mainWin = Tk_MainWindow(interp)) == 0) {
Tcl_AppendResult(interp, "Can't find main Tk window", (char *)NULL);
return TCL_ERROR;
}
/*
* Parse the arguments.
*/
/* HDC is required. */
if (objc < 2) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
dst = printDC;
/*
* Next, check to see if 'dst' can support BitBlt. If not, raise an
* error.
*/
if ((GetDeviceCaps(dst, RASTERCAPS) & RC_BITBLT) == 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"Can't do bitmap operations on device context\n"));
return TCL_ERROR;
}
/* Loop through the remaining arguments. */
for (k=2; k<objc; k++) {
if (strcmp(Tcl_GetString(objv[k]), "-window") == 0) {
if (Tcl_GetString(objv[k+1]) && Tcl_GetString(objv[k+1])[0] == '.') {
do_window = 1;
workwin = Tk_NameToWindow(interp, window_spec = Tcl_GetString(objv[++k]), mainWin);
if (workwin == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"Can't find window %s in this application",
window_spec));
return TCL_ERROR;
}
} else {
/* Use strtoul() so octal or hex representations will be
* parsed. */
hwnd = (HWND) INT2PTR(strtoul(Tcl_GetString(objv[++k]), &strend, 0));
if (strend == 0 || strend == Tcl_GetString(objv[k])) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"Can't understand window id %s", Tcl_GetString(objv[k])));
return TCL_ERROR;
}
}
} else if (strcmp(Tcl_GetString(objv[k]), "-screen") == 0) {
do_screen = 1;
wintype = PTScreen;
} else if (strcmp(Tcl_GetString(objv[k]), "-client") == 0) {
wintype = PTClient;
} else if (strcmp(Tcl_GetString(objv[k]), "-source") == 0) {
float a, b, c, d;
int count = sscanf(Tcl_GetString(objv[++k]), "%f%f%f%f", &a, &b, &c, &d);
if (count < 2) { /* Can't make heads or tails of it.... */
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
src_x = (int)a;
src_y = (int)b;
if (count == 4) {
src_w = (int)c;
src_h = (int)d;
}
} else if (strcmp(Tcl_GetString(objv[k]), "-destination") == 0) {
float a, b, c, d;
int count;
count = sscanf(Tcl_GetString(objv[++k]), "%f%f%f%f", &a, &b, &c, &d);
if (count < 2) { /* Can't make heads or tails of it.... */
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
dst_x = (int)a;
dst_y = (int)b;
if (count == 3) {
dst_w = (int)c;
dst_h = -1;
} else if (count == 4) {
dst_w = (int)c;
dst_h = (int)d;
}
} else if (strcmp(Tcl_GetString(objv[k]), "-scale") == 0) {
if (Tcl_GetString(objv[++k])) {
if (Tcl_GetDouble(interp, Tcl_GetString(objv[k]), &scale) != TCL_OK) {
return TCL_ERROR;
}
if (scale <= 0.01 || scale >= 100.0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"Unreasonable scale specification %s", Tcl_GetString(objv[k])));
return TCL_ERROR;
}
do_scale = 1;
}
} else if (strcmp(Tcl_GetString(objv[k]), "-noprint") == 0
|| strncmp(Tcl_GetString(objv[k]), "-calc", 5) == 0) {
/* This option suggested by Pascal Bouvier to get sizes without
* printing. */
do_print = 0;
}
}
/*
* Check to ensure no incompatible arguments were used.
*/
if (do_window && do_screen) {
Tcl_AppendResult(interp, usage_message, (char *)NULL);
return TCL_ERROR;
}
/*
* Get the MS Window we want to copy. Given the HDC, we can get the
* "Window".
*/
if (hwnd == 0) {
if (Tk_IsTopLevel(workwin)) {
is_toplevel = 1;
}
if ((wnd = Tk_WindowId(workwin)) == 0) {
Tcl_AppendResult(interp, "Can't get id for Tk window", (char *)NULL);
return TCL_ERROR;
}
/* Given the "Window" we can get a Microsoft Windows HWND. */
if ((hwnd = Tk_GetHWND(wnd)) == 0) {
Tcl_AppendResult(interp, "Can't get Windows handle for Tk window",
(char *)NULL);
return TCL_ERROR;
}
/*
* If it's a toplevel, give it special treatment: Get the top-level
* window instead. If the user only wanted the client, the -client
* flag will take care of it. This uses "windows" tricks rather than
* Tk since the obvious method of getting the wrapper window didn't
* seem to work.
*/
if (is_toplevel) {
HWND tmpWnd = hwnd;
while ((tmpWnd = GetParent(tmpWnd)) != 0) {
hwnd = tmpWnd;
}
}
}
/* Given the HWND, we can get the window's device context. */
if ((src = GetWindowDC(hwnd)) == 0) {
Tcl_AppendResult(interp, "Can't get device context for Tk window", (char *)NULL);
return TCL_ERROR;
}
if (do_screen) {
LONG w, h;
GetDisplaySize(&w, &h);
wid = w;
hgt = h;
} else if (is_toplevel) {
RECT tl;
GetWindowRect(hwnd, &tl);
wid = tl.right - tl.left;
hgt = tl.bottom - tl.top;
} else {
if ((hgt = Tk_Height(workwin)) <= 0) {
Tcl_AppendResult(interp, "Can't get height of Tk window", (char *)NULL);
ReleaseDC(hwnd,src);
return TCL_ERROR;
}
if ((wid = Tk_Width(workwin)) <= 0) {
Tcl_AppendResult(interp, "Can't get width of Tk window", (char *)NULL);
ReleaseDC(hwnd,src);
return TCL_ERROR;
}
}
/*
* Ensure all the widths and heights are set up right
* A: No dimensions are negative
* B: No dimensions exceed the maximums
* C: The dimensions don't lead to a 0 width or height image.
*/
if (src_x < 0) {
src_x = 0;
}
if (src_y < 0) {
src_y = 0;
}
if (dst_x < 0) {
dst_x = 0;
}
if (dst_y < 0) {
dst_y = 0;
}
if (src_w > wid || src_w <= 0) {
src_w = wid;
}
if (src_h > hgt || src_h <= 0) {
src_h = hgt;
}
if (do_scale && dst_w == 0) {
/* Calculate destination width and height based on scale. */
dst_w = (int)(scale * src_w);
dst_h = (int)(scale * src_h);
}
if (dst_h == -1) {
dst_h = (int) (((long)src_h * dst_w) / (src_w + 1)) + 1;
}
if (dst_h == 0 || dst_w == 0) {
dst_h = src_h;
dst_w = src_w;
}
if (do_print) {
/*
* Based on notes from Heiko Schock and Arndt Roger Schneider, create
* this as a DIBitmap, to allow output to a greater range of devices.
* This approach will also allow selection of
* a) Whole screen
* b) Whole window
* c) Client window only
* for the "grab"
*/
hDib = CopyToDIB(hwnd, wintype);
/* GdiFlush();. */
if (!hDib) {
Tcl_AppendResult(interp, "Can't create DIB", (char *)NULL);
ReleaseDC(hwnd,src);
return TCL_ERROR;
}
lpDIBHdr = (LPBITMAPINFOHEADER) GlobalLock(hDib);
if (!lpDIBHdr) {
Tcl_AppendResult(interp, "Can't get DIB header", (char *)NULL);
ReleaseDC(hwnd,src);
return TCL_ERROR;
}
lpBits = (LPSTR) lpDIBHdr + lpDIBHdr->biSize + DIBNumColors(lpDIBHdr) * sizeof(RGBQUAD);
/* stretch the DIBbitmap directly in the target device. */
if (StretchDIBits(dst,
dst_x, dst_y, dst_w, dst_h,
src_x, src_y, src_w, src_h,
lpBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS,
SRCCOPY) == (int)GDI_ERROR) {
errcode = GetLastError();
GlobalUnlock(hDib);
GlobalFree(hDib);
ReleaseDC(hwnd,src);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"StretchDIBits failed with code %ld", errcode));
return TCL_ERROR;
}
/* free allocated memory. */
GlobalUnlock(hDib);
GlobalFree(hDib);
}
ReleaseDC(hwnd,src);
/*
* The return value should relate to the size in the destination space.
* At least the height should be returned (for page layout purposes).
*/
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"%d %d %d %d", dst_x, dst_y, dst_w, dst_h));
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* DIBNumColors --
*
* Computes the number of colors required for a DIB palette.
*
* Results:
* Returns number of colors.
*
*----------------------------------------------------------------------
*/
static int DIBNumColors(
LPBITMAPINFOHEADER lpDIB)
{
WORD wBitCount; /* DIB bit count. */
DWORD dwClrUsed;
/*
* If this is a Windows-style DIB, the number of colors in the color table
* can be less than the number of bits per pixel allows for (i.e.
* lpbi->biClrUsed can be set to some value). If this is the case, return
* the appropriate value..
*/
dwClrUsed = lpDIB->biClrUsed;
if (dwClrUsed) {
return (WORD) dwClrUsed;
}
/*
* Calculate the number of colors in the color table based on.
* The number of bits per pixel for the DIB.
*/
wBitCount = lpDIB->biBitCount;
/* Return number of colors based on bits per pixel. */
switch (wBitCount) {
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
return 0;
}
}
/*
* Helper functions
*/
/*
* ParseFontWords converts various keywords to modifyers of a
* font specification.
* For all words, later occurrences override earlier occurrences.
* Overstrike and underline cannot be "undone" by other words
*/
/*
*----------------------------------------------------------------------
*
* GdiParseFontWords --
*
* Converts various keywords to modifiers of a font specification. For
* all words, later occurrences override earlier occurrences. Overstrike
* and underline cannot be "undone" by other words
*
* Results:
* Keywords converted to modifiers.
*
*----------------------------------------------------------------------
*/
static int GdiParseFontWords(
TCL_UNUSED(Tcl_Interp *),
LOGFONTW *lf,
const char *str[],
int numargs)
{
int i;
int retval = 0; /* Number of words that could not be parsed. */
for (i=0; i<numargs; i++) {
if (str[i]) {
int wt;
if ((wt = GdiWordToWeight(str[i])) != -1) {
lf->lfWeight = wt;
} else if (strcmp(str[i], "roman") == 0) {
lf->lfItalic = FALSE;
} else if (strcmp(str[i], "italic") == 0) {
lf->lfItalic = TRUE;
} else if (strcmp(str[i], "underline") == 0) {
lf->lfUnderline = TRUE;
} else if (strcmp(str[i], "overstrike") == 0) {
lf->lfStrikeOut = TRUE;
} else {
retval++;
}
}
}
return retval;
}
/*
*----------------------------------------------------------------------
*
* GdiWordToWeight --
*
* Converts keywords to font weights.
*
* Results:
* Helps set the proper font for GDI rendering.
*
*----------------------------------------------------------------------
*/
static int GdiWordToWeight(
const char *str)
{
int retval = -1;
size_t i;
static const struct font_weight {
const char *name;
int weight;
} font_weights[] = {
{ "thin", FW_THIN },
{ "extralight", FW_EXTRALIGHT },
{ "ultralight", FW_EXTRALIGHT },
{ "light", FW_LIGHT },
{ "normal", FW_NORMAL },
{ "regular", FW_NORMAL },
{ "medium", FW_MEDIUM },
{ "semibold", FW_SEMIBOLD },
{ "demibold", FW_SEMIBOLD },
{ "bold", FW_BOLD },
{ "extrabold", FW_EXTRABOLD },
{ "ultrabold", FW_EXTRABOLD },
{ "heavy", FW_HEAVY },
{ "black", FW_HEAVY },
};
if (str == 0) {
return -1;
}
for (i=0; i<sizeof(font_weights) / sizeof(struct font_weight); i++) {
if (strcmp(str, font_weights[i].name) == 0) {
retval = font_weights[i].weight;
break;
}
}
return retval;
}
/*
*----------------------------------------------------------------------
*
* MakeLogFont --
*
* Takes the font description string and converts this into a logical
* font spec.
*
* Results:
* Sets font weight.
*
*----------------------------------------------------------------------
*/
static int GdiMakeLogFont(
Tcl_Interp *interp,
const char *str,
LOGFONTW *lf,
HDC hDC)
{
const char **list;
Tcl_Size count;
/* Set up defaults for logical font. */
memset(lf, 0, sizeof(*lf));
lf->lfWeight = FW_NORMAL;
lf->lfCharSet = DEFAULT_CHARSET;
lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf->lfQuality = DEFAULT_QUALITY;
lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
/* The cast to (char *) is silly, based on prototype of Tcl_SplitList. */
if (Tcl_SplitList(interp, str, &count, &list) != TCL_OK) {
return 0;
}
/* Now we have the font structure broken into name, size, weight. */
if (count >= 1) {
Tcl_DString ds;
Tcl_DStringInit(&ds);
wcsncpy(lf->lfFaceName, Tcl_UtfToWCharDString(list[0], TCL_INDEX_NONE, &ds),
LF_FACESIZE-1);
Tcl_DStringFree(&ds);
lf->lfFaceName[LF_FACESIZE-1] = 0;
} else {
return 0;
}
if (count >= 2) {
int siz;
char *strend;
siz = strtol(list[1], &strend, 0);
/*
* Assumptions:
* 1) Like canvas, if a positive number is specified, it's in points.
* 2) Like canvas, if a negative number is specified, it's in pixels.
*/
if (strend > list[1]) { /* If it looks like a number, it is a number.... */
if (siz > 0) { /* Size is in points. */
SIZE wextent, vextent;
POINT worigin, vorigin;
double factor;
switch (GdiGetHdcInfo(hDC, &worigin, &wextent, &vorigin, &vextent)) {
case MM_ISOTROPIC:
if (vextent.cy < -1 || vextent.cy > 1) {
factor = (double)wextent.cy / vextent.cy;
if (factor < 0.0) {
factor = -factor;
}
lf->lfHeight = (int)(-siz * GetDeviceCaps(hDC, LOGPIXELSY) * factor / 72.0);
} else if (vextent.cx < -1 || vextent.cx > 1) {
factor = (double)wextent.cx / vextent.cx;
if (factor < 0.0) {
factor = -factor;
}
lf->lfHeight = (int)(-siz * GetDeviceCaps(hDC, LOGPIXELSY) * factor / 72.0);
} else {
lf->lfHeight = -siz; /* This is bad news.... */
}
break;
case MM_ANISOTROPIC:
if (vextent.cy != 0) {
factor = (double)wextent.cy / vextent.cy;
if (factor < 0.0) {
factor = -factor;
}
lf->lfHeight = (int)(-siz * GetDeviceCaps(hDC, LOGPIXELSY) * factor / 72.0);
} else {
lf->lfHeight = -siz; /* This is bad news.... */
}
break;
case MM_TEXT:
default:
/* If mapping mode is MM_TEXT, use the documented
* formula. */
lf->lfHeight = -MulDiv(siz, GetDeviceCaps(hDC, LOGPIXELSY), 72);
break;
case MM_HIENGLISH:
lf->lfHeight = -MulDiv(siz, 1000, 72);
break;
case MM_LOENGLISH:
lf->lfHeight = -MulDiv(siz, 100, 72);
break;
case MM_HIMETRIC:
lf->lfHeight = -MulDiv(siz, (int)(1000*2.54), 72);
break;
case MM_LOMETRIC:
lf->lfHeight = -MulDiv(siz, (int)(100*2.54), 72);
break;
case MM_TWIPS:
lf->lfHeight = -MulDiv(siz, 1440, 72);
break;
}
} else if (siz == 0) { /* Use default size of 12 points. */
lf->lfHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
} else { /* Use pixel size. */
lf->lfHeight = siz; /* Leave this negative. */
}
} else {
GdiParseFontWords(interp, lf, list+1, count-1);
}
}
if (count >= 3) {
GdiParseFontWords(interp, lf, list+2, count-2);
}
ckfree(list);
return 1;
}
/*
*----------------------------------------------------------------------
*
* GdiMakePen --
*
* Creates a logical pen based on input parameters and selects it into
* the hDC.
*
* Results:
* Sets rendering pen.
*
*----------------------------------------------------------------------
*/
static int GdiMakePen(
Tcl_Interp *interp,
double dwidth,
int dashstyle,
const char *dashstyledata,
TCL_UNUSED(int), /* Ignored for now. */
TCL_UNUSED(int), /* Ignored for now. */
TCL_UNUSED(int),
TCL_UNUSED(const char *), /* Ignored for now. */
unsigned long color,
HDC hDC,
HGDIOBJ *oldPen)
{
/*
* The LOGPEN structure takes the following dash options:
* PS_SOLID: a solid pen
* PS_DASH: a dashed pen
* PS_DOT: a dotted pen
* PS_DASHDOT: a pen with a dash followed by a dot
* PS_DASHDOTDOT: a pen with a dash followed by 2 dots
*
* It seems that converting to ExtCreatePen may be more advantageous, as
* it matches the Tk canvas pens much better--but not for Win95, which
* does not support PS_USERSTYLE. An explicit test (or storage in a static
* after first failure) may suffice for working around this. The
* ExtCreatePen is not supported at all under Win32.
*/
int width = floor(dwidth + 0.5);
HPEN hPen;
LOGBRUSH lBrush;
DWORD pStyle = PS_SOLID; /* -dash should override*/
DWORD endStyle = PS_ENDCAP_ROUND; /* -capstyle should override. */
DWORD joinStyle = PS_JOIN_ROUND; /* -joinstyle should override. */
DWORD styleCount = 0;
DWORD *styleArray = 0;
/*
* To limit the propagation of allocated memory, the dashes will have a
* maximum here. If one wishes to remove the static allocation, please be
* sure to update GdiFreePen and ensure that the array is NOT freed if the
* LOGPEN option is used.
*/
static DWORD pStyleData[24];
if (dashstyle != 0 && dashstyledata != 0) {
const char *cp;
size_t i;
char *dup = (char *) ckalloc(strlen(dashstyledata) + 1);
strcpy(dup, dashstyledata);
/* DEBUG. */
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"DEBUG: Found a dash spec of |%s|\n",
dashstyledata));
/* Parse the dash spec. */
if (isdigit(dashstyledata[0])) {
cp = strtok(dup, " \t,;");
for (i = 0; cp && i < sizeof(pStyleData) / sizeof(DWORD); i++) {
pStyleData[styleCount++] = atoi(cp);
cp = strtok(NULL, " \t,;");
}
} else {
for (i=0; dashstyledata[i] != '\0' && i< sizeof(pStyleData) / sizeof(DWORD); i++) {
switch (dashstyledata[i]) {
case ' ':
pStyleData[styleCount++] = 8;
break;
case ',':
pStyleData[styleCount++] = 4;
break;
case '_':
pStyleData[styleCount++] = 6;
break;
case '-':
pStyleData[styleCount++] = 4;
break;
case '.':
pStyleData[styleCount++] = 2;
break;
default:
break;
}
}
}
if (styleCount > 0) {
styleArray = pStyleData;
} else {
dashstyle = 0;
}
if (dup) {
ckfree(dup);
}
}
if (dashstyle != 0) {
pStyle = PS_USERSTYLE;
}
/* -stipple could affect this.... */
lBrush.lbStyle = BS_SOLID;
lBrush.lbColor = color;
lBrush.lbHatch = 0;
/* We only use geometric pens, even for 1-pixel drawing. */
hPen = ExtCreatePen(PS_GEOMETRIC|pStyle|endStyle|joinStyle,
width, &lBrush, styleCount, styleArray);
if (hPen == 0) { /* Failed for some reason...Fall back on CreatePenIndirect. */
LOGPEN lf;
lf.lopnWidth.x = width;
lf.lopnWidth.y = 0; /* Unused in LOGPEN. */
if (dashstyle == 0) {
lf.lopnStyle = PS_SOLID; /* For now...convert 'style' in the future. */
} else {
lf.lopnStyle = PS_DASH; /* REALLLLY simple for now. */
}
lf.lopnColor = color; /* Assume we're getting a COLORREF. */
/* Now we have a logical pen. Create the "real" pen and put it in the
* hDC. */
hPen = CreatePenIndirect(&lf);
}
*oldPen = SelectObject(hDC, hPen);
return 1;
}
/*
*----------------------------------------------------------------------
*
* GdiFreePen --
*
* Wraps the protocol to delete a created pen.
*
* Results:
* Deletes pen.
*
*----------------------------------------------------------------------
*/
static int GdiFreePen(
TCL_UNUSED(Tcl_Interp *),
HDC hDC,
HGDIOBJ oldPen)
{
HGDIOBJ gonePen = SelectObject(hDC, oldPen);
DeleteObject(gonePen);
return 1;
}
/*
*----------------------------------------------------------------------
*
* GdiMakeBrush--
*
* Creates a logical brush based on input parameters, and selects it into
* the hdc.
*
* Results:
* Creates brush.
*
*----------------------------------------------------------------------
*/
static int GdiMakeBrush(
unsigned long color,
long hatch,
LOGBRUSH *lb,
HDC hDC,
HBRUSH *oldBrush)
{
HBRUSH hBrush;
lb->lbStyle = BS_SOLID; /* Support other styles later. */
lb->lbColor = color; /* Assume this is a COLORREF. */
lb->lbHatch = hatch; /* Ignored for now, given BS_SOLID in the Style. */
/* Now we have the logical brush. Create the "real" brush and put it in
* the hDC. */
hBrush = CreateBrushIndirect(lb);
*oldBrush = (HBRUSH)SelectObject(hDC, hBrush);
return 1;
}
/*
*----------------------------------------------------------------------
*
* GdiFreeBrush --
*
* Wraps the protocol to delete a created brush.
*
* Results:
* Deletes brush.
*
*----------------------------------------------------------------------
*/
static void GdiFreeBrush(
TCL_UNUSED(Tcl_Interp *),
HDC hDC,
HGDIOBJ oldBrush)
{
HGDIOBJ goneBrush;
goneBrush = SelectObject(hDC, oldBrush);
DeleteObject(goneBrush);
}
/*
* Utility functions from elsewhere in Tcl.
* Functions have removed reliance on X and Tk libraries, as well as removing
* the need for TkWindows.
* GdiGetColor is a copy of a TkpGetColor from tkWinColor.c
*/
typedef struct {
const char *name;
int index;
} SystemColorEntry;
static const SystemColorEntry sysColors[] = {
{"3dDarkShadow", COLOR_3DDKSHADOW},
{"3dLight", COLOR_3DLIGHT},
{"ActiveBorder", COLOR_ACTIVEBORDER},
{"ActiveCaption", COLOR_ACTIVECAPTION},
{"AppWorkspace", COLOR_APPWORKSPACE},
{"Background", COLOR_BACKGROUND},
{"ButtonFace", COLOR_BTNFACE},
{"ButtonHighlight", COLOR_BTNHIGHLIGHT},
{"ButtonShadow", COLOR_BTNSHADOW},
{"ButtonText", COLOR_BTNTEXT},
{"CaptionText", COLOR_CAPTIONTEXT},
{"DisabledText", COLOR_GRAYTEXT},
{"GrayText", COLOR_GRAYTEXT},
{"Highlight", COLOR_HIGHLIGHT},
{"HighlightText", COLOR_HIGHLIGHTTEXT},
{"InactiveBorder", COLOR_INACTIVEBORDER},
{"InactiveCaption", COLOR_INACTIVECAPTION},
{"InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT},
{"InfoBackground", COLOR_INFOBK},
{"InfoText", COLOR_INFOTEXT},
{"Menu", COLOR_MENU},
{"MenuText", COLOR_MENUTEXT},
{"Scrollbar", COLOR_SCROLLBAR},
{"Window", COLOR_WINDOW},
{"WindowFrame", COLOR_WINDOWFRAME},
{"WindowText", COLOR_WINDOWTEXT}
};
static const size_t numsyscolors = sizeof(sysColors) / sizeof(SystemColorEntry);
/*
*----------------------------------------------------------------------
*
* GdiGetColor --
*
* Convert color name to color specification.
*
* Results:
* Color name converted.
*
*----------------------------------------------------------------------
*/
static int GdiGetColor(
Tcl_Obj *nameObj,
COLORREF *color)
{
const char *name = Tcl_GetString(nameObj);
if (_strnicmp(name, "system", 6) == 0) {
size_t i, l, u;
int r;
l = 0;
u = numsyscolors;
while (l <= u) {
i = (l + u) / 2;
if ((r = _strcmpi(name+6, sysColors[i].name)) == 0) {
break;
}
if (r < 0) {
u = i - 1;
} else {
l = i + 1;
}
}
if (l > u) {
return 0;
}
*color = GetSysColor(sysColors[i].index);
return 1;
} else {
int result;
XColor xcolor;
result = XParseColor(NULL, 0, name, &xcolor);
*color = ((xcolor.red & 0xFF00)>>8) | (xcolor.green & 0xFF00)
| ((xcolor.blue & 0xFF00)<<8);
return result;
}
}
/*
* Beginning of functions for screen-to-dib translations.
*
* Several of these functions are based on those in the WINCAP32 program
* provided as a sample by Microsoft on the VC++ 5.0 disk. The copyright on
* these functions is retained, even for those with significant changes.
*/
/*
*----------------------------------------------------------------------
*
* CopyToDIB --
*
* Copy window bits to a DIB.
*
* Results:
* Color specification converted.
*
*----------------------------------------------------------------------
*/
static HANDLE CopyToDIB(
HWND hWnd,
enum PrintType type)
{
HANDLE hDIB;
HBITMAP hBitmap;
HPALETTE hPalette;
/* Check for a valid window handle. */
if (!hWnd) {
return NULL;
}
switch (type) {
case PTWindow: { /* Copy entire window. */
RECT rectWnd;
/* Get the window rectangle. */
GetWindowRect(hWnd, &rectWnd);
/*
* Get the DIB of the window by calling CopyScreenToDIB and passing it
* the window rect.
*/
hDIB = CopyScreenToDIB(&rectWnd);
break;
}
case PTClient: { /* Copy client area. */
RECT rectClient;
POINT pt1, pt2;
/* Get the client area dimensions. */
GetClientRect(hWnd, &rectClient);
/* Convert client coords to screen coords. */
pt1.x = rectClient.left;
pt1.y = rectClient.top;
pt2.x = rectClient.right;
pt2.y = rectClient.bottom;
ClientToScreen(hWnd, &pt1);
ClientToScreen(hWnd, &pt2);
rectClient.left = pt1.x;
rectClient.top = pt1.y;
rectClient.right = pt2.x;
rectClient.bottom = pt2.y;
/*
* Get the DIB of the client area by calling CopyScreenToDIB and
* passing it the client rect.
*/
hDIB = CopyScreenToDIB(&rectClient);
break;
}
case PTScreen: { /* Entire screen. */
RECT Rect;
/*
* Get the device-dependent bitmap in lpRect by calling
* CopyScreenToBitmap and passing it the rectangle to grab.
*/
Rect.top = Rect.left = 0;
GetDisplaySize(&Rect.right, &Rect.bottom);
hBitmap = CopyScreenToBitmap(&Rect);
/* Check for a valid bitmap handle. */
if (!hBitmap) {
return NULL;
}
/* Get the current palette. */
hPalette = GetSystemPalette();
/* Convert the bitmap to a DIB. */
hDIB = BitmapToDIB(hBitmap, hPalette);
/* Clean up. */
DeleteObject(hPalette);
DeleteObject(hBitmap);
/* Return handle to the packed-DIB. */
break;
}
default: /* Invalid print area. */
return NULL;
}
/* Return the handle to the DIB. */
return hDIB;
}
/*
*----------------------------------------------------------------------
*
* GetDisplaySize--
*
* GetDisplaySize does just that. There may be an easier way, but it is
* not apparent.
*
* Results:
* Returns display size.
*
*----------------------------------------------------------------------
*/
static void GetDisplaySize(
LONG *width,
LONG *height)
{
HDC hDC;
hDC = CreateDCW(L"DISPLAY", 0, 0, 0);
*width = GetDeviceCaps(hDC, HORZRES);
*height = GetDeviceCaps(hDC, VERTRES);
DeleteDC(hDC);
}
/*
*----------------------------------------------------------------------
*
* CopyScreenToBitmap--
*
* Copies screen to bitmap.
*
* Results:
* Screen is copied.
*
*----------------------------------------------------------------------
*/
static HBITMAP CopyScreenToBitmap(
LPRECT lpRect)
{
HDC hScrDC, hMemDC; /* Screen DC and memory DC. */
HGDIOBJ hBitmap, hOldBitmap; /* Handles to deice-dependent bitmaps. */
int nX, nY, nX2, nY2; /* Coordinates of rectangle to grab. */
int nWidth, nHeight; /* DIB width and height */
int xScrn, yScrn; /* Screen resolution. */
/* Check for an empty rectangle. */
if (IsRectEmpty(lpRect)) {
return NULL;
}
/*
* Create a DC for the screen and create a memory DC compatible to screen
* DC.
*/
hScrDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
hMemDC = CreateCompatibleDC(hScrDC);
/* Get points of rectangle to grab. */
nX = lpRect->left;
nY = lpRect->top;
nX2 = lpRect->right;
nY2 = lpRect->bottom;
/* Get screen resolution. */
xScrn = GetDeviceCaps(hScrDC, HORZRES);
yScrn = GetDeviceCaps(hScrDC, VERTRES);
/* Make sure bitmap rectangle is visible. */
if (nX < 0) {
nX = 0;
}
if (nY < 0) {
nY = 0;
}
if (nX2 > xScrn) {
nX2 = xScrn;
}
if (nY2 > yScrn) {
nY2 = yScrn;
}
nWidth = nX2 - nX;
nHeight = nY2 - nY;
/* Create a bitmap compatible with the screen DC. */
hBitmap = CreateCompatibleBitmap(hScrDC, nWidth, nHeight);
/* Select new bitmap into memory DC. */
hOldBitmap = SelectObject(hMemDC, hBitmap);
/* Bitblt screen DC to memory DC. */
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY);
/*
* Select old bitmap back into memory DC and get handle to bitmap of the
* screen.
*/
hBitmap = SelectObject(hMemDC, hOldBitmap);
/* Clean up. */
DeleteDC(hScrDC);
DeleteDC(hMemDC);
/* Return handle to the bitmap. */
return (HBITMAP)hBitmap;
}
/*
*----------------------------------------------------------------------
*
* BitmapToDIB--
*
* Converts bitmap to DIB.
*
* Results:
* Bitmap converted.
*
*----------------------------------------------------------------------
*/
static HANDLE BitmapToDIB(
HBITMAP hBitmap,
HPALETTE hPal)
{
BITMAP bm;
BITMAPINFOHEADER bi;
LPBITMAPINFOHEADER lpbi;
DWORD dwLen;
HANDLE hDIB;
HANDLE h;
HDC hDC;
WORD biBits;
/* Check if bitmap handle is valid. */
if (!hBitmap) {
return NULL;
}
/* Fill in BITMAP structure, return NULL if it didn't work. */
if (!GetObjectW(hBitmap, sizeof(bm), (LPWSTR)&bm)) {
return NULL;
}
/* Ff no palette is specified, use default palette. */
if (hPal == NULL) {
hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
}
/* Calculate bits per pixel. */
biBits = bm.bmPlanes * bm.bmBitsPixel;
/* Make sure bits per pixel is valid. */
if (biBits <= 1) {
biBits = 1;
} else if (biBits <= 4) {
biBits = 4;
} else if (biBits <= 8) {
biBits = 8;
} else { /* If greater than 8-bit, force to 24-bit. */
biBits = 24;
}
/* Initialize BITMAPINFOHEADER. */
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = biBits;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
/* Calculate size of memory block required to store BITMAPINFO. */
dwLen = bi.biSize + DIBNumColors(&bi) * sizeof(RGBQUAD);
/* Get a DC. */
hDC = GetDC(NULL);
/* Select and realize our palette. */
hPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);
/* Alloc memory block to store our bitmap. */
hDIB = GlobalAlloc(GHND, dwLen);
/* If we couldn't get memory block. */
if (!hDIB) {
/* clean up and return NULL. */
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}
/* Lock memory and get pointer to it. */
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
/* Use our bitmap info. to fill BITMAPINFOHEADER. */
*lpbi = bi;
/* Call GetDIBits with a NULL lpBits param, so it will calculate the
* biSizeImage field for us
*/
GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS);
/* get the info. returned by GetDIBits and unlock memory block. */
bi = *lpbi;
GlobalUnlock(hDIB);
/* If the driver did not fill in the biSizeImage field, make one up. */
if (bi.biSizeImage == 0) {
bi.biSizeImage = (((((DWORD)bm.bmWidth * biBits) + 31) / 32) * 4)
* bm.bmHeight;
}
/* Realloc the buffer big enough to hold all the bits. */
dwLen = bi.biSize + DIBNumColors(&bi) * sizeof(RGBQUAD) + bi.biSizeImage;
if ((h = GlobalReAlloc(hDIB, dwLen, 0)) != 0) {
hDIB = h;
} else {
/* Clean up and return NULL. */
GlobalFree(hDIB);
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}
/* Lock memory block and get pointer to it. */
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
/* Call GetDIBits with a NON-NULL lpBits param, and actualy get the
* bits this time.
*/
if (GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, (LPSTR)lpbi +
(WORD)lpbi->biSize + DIBNumColors(lpbi) * sizeof(RGBQUAD),
(LPBITMAPINFO)lpbi, DIB_RGB_COLORS) == 0) {
/* Clean up and return NULL. */
GlobalUnlock(hDIB);
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return NULL;
}
bi = *lpbi;
/* Clean up. */
GlobalUnlock(hDIB);
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
/* Return handle to the DIB. */
return hDIB;
}
/*
*----------------------------------------------------------------------
*
* CopyScreenToDIB--
*
* Copies screen to DIB.
*
* Results:
* Screen copied.
*
*----------------------------------------------------------------------
*/
static HANDLE CopyScreenToDIB(
LPRECT lpRect)
{
HBITMAP hBitmap;
HPALETTE hPalette;
HANDLE hDIB;
/*
* Get the device-dependent bitmap in lpRect by calling CopyScreenToBitmap
* and passing it the rectangle to grab.
*/
hBitmap = CopyScreenToBitmap(lpRect);
/* Check for a valid bitmap handle. */
if (!hBitmap) {
return NULL;
}
/* Get the current palette. */
hPalette = GetSystemPalette();
/* convert the bitmap to a DIB. */
hDIB = BitmapToDIB(hBitmap, hPalette);
/* Clean up. */
DeleteObject(hPalette);
DeleteObject(hBitmap);
/* Return handle to the packed-DIB. */
return hDIB;
}
/*
*----------------------------------------------------------------------
*
* GetSystemPalette--
*
* Obtains the system palette.
*
* Results:
* Returns palette.
*
*----------------------------------------------------------------------
*/
static HPALETTE GetSystemPalette(void)
{
HDC hDC; /* Handle to a DC. */
static HPALETTE hPal = NULL; /* Handle to a palette. */
HANDLE hLogPal; /* Handle to a logical palette. */
LPLOGPALETTE lpLogPal; /* Pointer to a logical palette. */
int nColors; /* Number of colors. */
/* Find out how many palette entries we want.. */
hDC = GetDC(NULL);
if (!hDC) {
return NULL;
}
nColors = PalEntriesOnDevice(hDC); /* Number of palette entries. */
/* Allocate room for the palette and lock it.. */
hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors *
sizeof(PALETTEENTRY));
if (!hLogPal) {
/* If we didn't get a logical palette, return NULL. */
return NULL;
}
/* get a pointer to the logical palette. */
lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal);
/* Set some important fields. */
lpLogPal->palVersion = 0x300;
lpLogPal->palNumEntries = nColors;
/* Copy the current system palette into our logical palette. */
GetSystemPaletteEntries(hDC, 0, nColors,
(LPPALETTEENTRY) lpLogPal->palPalEntry);
/*
* Go ahead and create the palette. Once it's created, we no longer need
* the LOGPALETTE, so free it.
*/
hPal = CreatePalette(lpLogPal);
/* Clean up. */
GlobalUnlock(hLogPal);
GlobalFree(hLogPal);
ReleaseDC(NULL, hDC);
return hPal;
}
/*
*----------------------------------------------------------------------
*
* PalEntriesOnDevice--
*
* Returns the palettes on the device.
*
* Results:
* Returns palettes.
*
*----------------------------------------------------------------------
*/
static int PalEntriesOnDevice(
HDC hDC)
{
return (1 << (GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES)));
}
/*
* --------------------------------------------------------------------------
*
* Winprint_Init--
*
* Initializes printing module on Windows.
*
* Results:
* Module initialized.
*
* -------------------------------------------------------------------------
*/
int Winprint_Init(
Tcl_Interp * interp)
{
size_t i;
Tcl_Namespace *namespacePtr;
static const char *gdiName = "::tk::print::_gdi";
static const size_t numCommands =
sizeof(gdi_commands) / sizeof(struct gdi_command);
/*
* Set up the low-level [_gdi] command.
*/
namespacePtr = Tcl_CreateNamespace(interp, gdiName,
NULL, (Tcl_NamespaceDeleteProc *) NULL);
for (i=0; i<numCommands; i++) {
char buffer[100];
snprintf(buffer, sizeof(buffer), "%s::%s", gdiName, gdi_commands[i].command_string);
Tcl_CreateObjCommand2(interp, buffer, gdi_commands[i].command,
NULL, (Tcl_CmdDeleteProc *) 0);
Tcl_Export(interp, namespacePtr, gdi_commands[i].command_string, 0);
}
Tcl_CreateEnsemble(interp, gdiName, namespacePtr, 0);
/*
* The other printing-related commands.
*/
Tcl_CreateObjCommand2(interp, "::tk::print::_selectprinter",
PrintSelectPrinter, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_openprinter",
PrintOpenPrinter, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_closeprinter",
PrintClosePrinter, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_opendoc",
PrintOpenDoc, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_closedoc",
PrintCloseDoc, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_openpage",
PrintOpenPage, NULL, NULL);
Tcl_CreateObjCommand2(interp, "::tk::print::_closepage",
PrintClosePage, NULL, NULL);
return TCL_OK;
}
/* Print API functions. */
/*----------------------------------------------------------------------
*
* PrintSelectPrinter--
*
* Main dialog for selecting printer and initializing data for print job.
*
* Results:
* Printer selected.
*
*----------------------------------------------------------------------
*/
static int PrintSelectPrinter(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj* const*))
{
LPCWSTR printerName = NULL;
PDEVMODEW returnedDevmode = NULL;
PDEVMODEW localDevmode = NULL;
copies = 0;
paper_width = 0;
paper_height = 0;
dpi_x = 0;
dpi_y = 0;
/* Set up print dialog and initalize property structure. */
memset(&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner = GetDesktopWindow();
pd.Flags = PD_HIDEPRINTTOFILE | PD_DISABLEPRINTTOFILE | PD_NOSELECTION;
if (PrintDlgW(&pd) == TRUE) {
/*Get document info.*/
memset(&di, 0, sizeof(di));
di.cbSize = sizeof(di);
di.lpszDocName = L"Tk Print Output";
/* Copy print attributes to local structure. */
returnedDevmode = (PDEVMODEW) GlobalLock(pd.hDevMode);
devnames = (LPDEVNAMES) GlobalLock(pd.hDevNames);
printerName = (LPCWSTR) devnames + devnames->wDeviceOffset;
localDevmode = (LPDEVMODEW) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY | HEAP_GENERATE_EXCEPTIONS,
returnedDevmode->dmSize);
if (localDevmode != NULL) {
memcpy((LPVOID)localDevmode, (LPVOID)returnedDevmode,
returnedDevmode->dmSize);
/* Get values from user-set and built-in properties. */
localPrinterName = localDevmode->dmDeviceName;
dpi_y = localDevmode->dmYResolution;
dpi_x = localDevmode->dmPrintQuality;
/* Convert height and width to logical points. */
paper_height = (int) localDevmode->dmPaperLength / 0.254;
paper_width = (int) localDevmode->dmPaperWidth / 0.254;
copies = pd.nCopies;
/* Set device context here for all GDI printing operations. */
printDC = CreateDCW(L"WINSPOOL", printerName, NULL, localDevmode);
} else {
localDevmode = NULL;
}
} else {
unsigned int errorcode = CommDlgExtendedError();
/*
* The user cancelled, or there was an error
* The code on the Tcl side checks if the variable
* ::tk::print::printer_name is defined to determine
* that a valid selection was made.
* So we better unset this here, unconditionally.
*/
Tcl_UnsetVar(interp, "::tk::print::printer_name", 0);
if (errorcode != 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("print failed: error %04x",
errorcode));
Tcl_SetErrorCode(interp, "TK", "PRINT", "DIALOG", (char*)NULL);
return TCL_ERROR;
}
return TCL_OK;
}
if (pd.hDevMode != NULL) {
GlobalFree(pd.hDevMode);
}
/*
* Store print properties in variables so they can be accessed from
* script level.
*/
if (localPrinterName != NULL) {
char *prname;
int size_needed = WideCharToMultiByte(CP_UTF8, 0, localPrinterName,
-1, NULL, 0, NULL, NULL);
prname = (char*)ckalloc(size_needed);
WideCharToMultiByte(CP_UTF8, 0, localPrinterName, -1, prname,
size_needed, NULL, NULL);
Tcl_SetVar2Ex(interp, "::tk::print::printer_name", NULL,
Tcl_NewStringObj(prname, size_needed - 1), 0);
Tcl_SetVar2Ex(interp, "::tk::print::copies", NULL,
Tcl_NewIntObj(copies), 0);
Tcl_SetVar2Ex(interp, "::tk::print::dpi_x", NULL,
Tcl_NewIntObj(dpi_x), 0);
Tcl_SetVar2Ex(interp, "::tk::print::dpi_y", NULL,
Tcl_NewIntObj(dpi_y), 0);
Tcl_SetVar2Ex(interp, "::tk::print::paper_width", NULL,
Tcl_NewIntObj(paper_width), 0);
Tcl_SetVar2Ex(interp, "::tk::print::paper_height", NULL,
Tcl_NewIntObj(paper_height), 0);
ckfree(prname);
}
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintOpenPrinter--
*
* Open the given printer.
*
* Results:
* Opens the selected printer.
*
* -------------------------------------------------------------------------
*/
int PrintOpenPrinter(
TCL_UNUSED(void *),
Tcl_Interp *interp,
Tcl_Size objc,
Tcl_Obj *const objv[])
{
Tcl_DString ds;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "printer");
return TCL_ERROR;
}
/*Start an individual page.*/
if (StartPage(printDC) <= 0) {
return TCL_ERROR;
}
const char *printer = Tcl_GetString(objv[1]);
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
Tcl_DStringInit(&ds);
if ((OpenPrinterW(Tcl_UtfToWCharDString(printer, -1, &ds),
(LPHANDLE)&printDC, NULL)) == FALSE) {
Tcl_AppendResult(interp, "unable to open printer", (char *)NULL);
Tcl_DStringFree(&ds);
return TCL_ERROR;
}
Tcl_DStringFree(&ds);
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintClosePrinter--
*
* Closes the given printer.
*
* Results:
* Printer closed.
*
* -------------------------------------------------------------------------
*/
int PrintClosePrinter(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj *const *))
{
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
ClosePrinter(printDC);
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintOpenDoc--
*
* Opens the document for printing.
*
* Results:
* Opens the print document.
*
* -------------------------------------------------------------------------
*/
int PrintOpenDoc(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj *const *))
{
int output = 0;
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
/*
* Start printing.
*/
output = StartDocW(printDC, &di);
if (output <= 0) {
Tcl_AppendResult(interp, "unable to start document", (char *)NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintCloseDoc--
*
* Closes the document for printing.
*
* Results:
* Closes the print document.
*
* -------------------------------------------------------------------------
*/
int PrintCloseDoc(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj *const *))
{
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
if (EndDoc(printDC) <= 0) {
Tcl_AppendResult(interp, "unable to establish close document", (char *)NULL);
return TCL_ERROR;
}
DeleteDC(printDC);
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintOpenPage--
*
* Opens a page for printing.
*
* Results:
* Opens the print page.
*
* -------------------------------------------------------------------------
*/
int PrintOpenPage(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj *const *))
{
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
/*Start an individual page.*/
if (StartPage(printDC) <= 0) {
Tcl_AppendResult(interp, "unable to start page", (char *)NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* --------------------------------------------------------------------------
*
* PrintClosePage--
*
* Closes the printed page.
*
* Results:
* Closes the page.
*
* -------------------------------------------------------------------------
*/
int PrintClosePage(
TCL_UNUSED(void *),
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Size),
TCL_UNUSED(Tcl_Obj *const *))
{
if (printDC == NULL) {
Tcl_AppendResult(interp, "unable to establish device context", (char *)NULL);
return TCL_ERROR;
}
if (EndPage(printDC) <= 0) {
Tcl_AppendResult(interp, "unable to close page", (char *)NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
* Local Variables:
* mode: c
* c-basic-offset: 4
* fill-column: 78
* End:
*/