blob: 15601391abe4b9f45c73c2cb9a184effc4a83453 [file] [log] [blame]
/*
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "awt_Component.h"
#include "awt_PrintControl.h"
#include "awt.h"
#include "awt_PrintDialog.h"
#include <winspool.h>
#include <float.h>
#include <math.h>
#define ROUNDTOINT(x) ((int)((x)+0.5))
static const int DEFAULT_RES = 72;
static const double TENTHS_MM_TO_POINTS = 3.527777778;
static const double LOMETRIC_TO_POINTS = (72.0 / 254.0);
/* Values must match those defined in WPrinterJob.java */
static const DWORD SET_COLOR = 0x00000200;
static const DWORD SET_ORIENTATION = 0x00004000;
static const DWORD SET_DUP_VERTICAL = 0x00000010;
static const DWORD SET_DUP_HORIZONTAL = 0x00000020;
static const DWORD SET_RES_HIGH = 0x00000040;
static const DWORD SET_RES_LOW = 0x00000080;
/* These methods and fields are on sun.awt.windows.WPrinterJob */
jfieldID AwtPrintControl::dialogOwnerPeerID;
jmethodID AwtPrintControl::getPrintDCID;
jmethodID AwtPrintControl::setPrintDCID;
jmethodID AwtPrintControl::getDevmodeID;
jmethodID AwtPrintControl::setDevmodeID;
jmethodID AwtPrintControl::getDevnamesID;
jmethodID AwtPrintControl::setDevnamesID;
jmethodID AwtPrintControl::getParentWindowID;
jfieldID AwtPrintControl::driverDoesMultipleCopiesID;
jfieldID AwtPrintControl::driverDoesCollationID;
jmethodID AwtPrintControl::getWin32MediaID;
jmethodID AwtPrintControl::setWin32MediaID;
jmethodID AwtPrintControl::getWin32MediaTrayID;
jmethodID AwtPrintControl::setWin32MediaTrayID;
jmethodID AwtPrintControl::getColorID;
jmethodID AwtPrintControl::getCopiesID;
jmethodID AwtPrintControl::getSelectID;
jmethodID AwtPrintControl::getDestID;
jmethodID AwtPrintControl::getDialogID;
jmethodID AwtPrintControl::getFromPageID;
jmethodID AwtPrintControl::getMaxPageID;
jmethodID AwtPrintControl::getMinPageID;
jmethodID AwtPrintControl::getCollateID;
jmethodID AwtPrintControl::getOrientID;
jmethodID AwtPrintControl::getQualityID;
jmethodID AwtPrintControl::getPrintToFileEnabledID;
jmethodID AwtPrintControl::getPrinterID;
jmethodID AwtPrintControl::setPrinterID;
jmethodID AwtPrintControl::getResID;
jmethodID AwtPrintControl::getSidesID;
jmethodID AwtPrintControl::getToPageID;
jmethodID AwtPrintControl::setToPageID;
jmethodID AwtPrintControl::setNativeAttID;
jmethodID AwtPrintControl::setRangeCopiesID;
jmethodID AwtPrintControl::setResID;
jmethodID AwtPrintControl::setJobAttributesID;
BOOL AwtPrintControl::IsSupportedLevel(HANDLE hPrinter, DWORD dwLevel) {
BOOL isSupported = FALSE;
DWORD cbBuf = 0;
LPBYTE pPrinter = NULL;
DASSERT(hPrinter != NULL);
VERIFY(::GetPrinter(hPrinter, dwLevel, NULL, 0, &cbBuf) == 0);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
pPrinter = new BYTE[cbBuf];
if (::GetPrinter(hPrinter, dwLevel, pPrinter, cbBuf, &cbBuf)) {
isSupported = TRUE;
}
delete[] pPrinter;
}
return isSupported;
}
BOOL AwtPrintControl::FindPrinter(jstring printerName, LPBYTE pPrinterEnum,
LPDWORD pcbBuf, LPTSTR * foundPrinter,
LPTSTR * foundPort)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
DWORD cReturned = 0;
if (pPrinterEnum == NULL) {
// Compute size of buffer
DWORD cbNeeded = 0;
::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
NULL, 2, NULL, 0, &cbNeeded, &cReturned);
::EnumPrinters(PRINTER_ENUM_LOCAL,
NULL, 5, NULL, 0, pcbBuf, &cReturned);
if (cbNeeded > (*pcbBuf)) {
*pcbBuf = cbNeeded;
}
return TRUE;
}
DASSERT(printerName != NULL);
DWORD cbBuf = *pcbBuf, dummyWord = 0;
JavaStringBuffer printerNameBuf(env, printerName);
LPTSTR lpcPrinterName = (LPTSTR)printerNameBuf;
DASSERT(lpcPrinterName != NULL);
// For NT, first do a quick check of all remote and local printers.
// This only allows us to search by name, though. PRINTER_INFO_4
// doesn't support port searches. So, if the user has specified the
// printer name as "LPT1:" (even though this is actually a port
// name), we won't find the printer here.
if (!::EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
NULL, 4, pPrinterEnum, cbBuf, &dummyWord, &cReturned)) {
return FALSE;
}
for (DWORD i = 0; i < cReturned; i++) {
PRINTER_INFO_4 *info4 = (PRINTER_INFO_4 *)
(pPrinterEnum + i * sizeof(PRINTER_INFO_4));
if (info4->pPrinterName != NULL &&
_tcsicmp(lpcPrinterName, info4->pPrinterName) == 0) {
// Fix for BugTraq Id 4281380.
// Get the port name since some drivers may require
// this name to be passed to ::DeviceCapabilities().
HANDLE hPrinter = NULL;
if (::OpenPrinter(info4->pPrinterName, &hPrinter, NULL)) {
// Fix for BugTraq Id 4286812.
// Some drivers don't support PRINTER_INFO_5.
// In this case we try PRINTER_INFO_2, and if that
// isn't supported as well return NULL port name.
try {
if (AwtPrintControl::IsSupportedLevel(hPrinter, 5)) {
VERIFY(::GetPrinter(hPrinter, 5, pPrinterEnum, cbBuf,
&dummyWord));
PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)pPrinterEnum;
*foundPrinter = info5->pPrinterName;
// pPortName may specify multiple ports. We only want one.
*foundPort = (info5->pPortName != NULL)
? _tcstok(info5->pPortName, TEXT(",")) : NULL;
} else if (AwtPrintControl::IsSupportedLevel(hPrinter, 2)) {
VERIFY(::GetPrinter(hPrinter, 2, pPrinterEnum, cbBuf,
&dummyWord));
PRINTER_INFO_2 *info2 = (PRINTER_INFO_2 *)pPrinterEnum;
*foundPrinter = info2->pPrinterName;
// pPortName may specify multiple ports. We only want one.
*foundPort = (info2->pPortName != NULL)
? _tcstok(info2->pPortName, TEXT(",")) : NULL;
} else {
*foundPrinter = info4->pPrinterName;
// We failed to determine port name for the found printer.
*foundPort = NULL;
}
} catch (std::bad_alloc&) {
VERIFY(::ClosePrinter(hPrinter));
throw;
}
VERIFY(::ClosePrinter(hPrinter));
return TRUE;
}
return FALSE;
}
}
// We still haven't found the printer, /* or we're using 95/98. */
// PRINTER_INFO_5 supports both printer name and port name, so
// we'll test both. On NT, PRINTER_ENUM_LOCAL means just local
// printers. This is what we want, because we already tested all
// remote printer names above (and remote printer port names are
// the same as remote printer names). On 95/98, PRINTER_ENUM_LOCAL
// means both remote and local printers. This is also what we want
// because we haven't tested any printers yet.
if (!::EnumPrinters(PRINTER_ENUM_LOCAL,
NULL, 5, pPrinterEnum, cbBuf, &dummyWord, &cReturned)) {
return FALSE;
}
for (DWORD i = 0; i < cReturned; i++) {
PRINTER_INFO_5 *info5 = (PRINTER_INFO_5 *)
(pPrinterEnum + i * sizeof(PRINTER_INFO_5));
// pPortName can specify multiple ports. Test them one at
// a time.
if (info5->pPortName != NULL) {
LPTSTR port = _tcstok(info5->pPortName, TEXT(","));
while (port != NULL) {
if (_tcsicmp(lpcPrinterName, port) == 0) {
*foundPrinter = info5->pPrinterName;
*foundPort = port;
return TRUE;
}
port = _tcstok(NULL, TEXT(","));
}
}
}
return FALSE;
}
void AwtPrintControl::initIDs(JNIEnv *env, jclass cls)
{
TRY;
jclass cls = env->FindClass("sun/awt/windows/WPrinterJob");
CHECK_NULL(cls);
AwtPrintControl::dialogOwnerPeerID =
env->GetFieldID(cls, "dialogOwnerPeer", "Ljava/awt/peer/ComponentPeer;");
DASSERT(AwtPrintControl::dialogOwnerPeerID != NULL);
CHECK_NULL(AwtPrintControl::dialogOwnerPeerID);
AwtPrintControl::getParentWindowID = env->GetMethodID(cls,
"getParentWindowID", "()J");
DASSERT(AwtPrintControl::getParentWindowID != NULL);
CHECK_NULL(AwtPrintControl::getParentWindowID);
AwtPrintControl::getPrintDCID = env->GetMethodID(cls, "getPrintDC", "()J");
DASSERT(AwtPrintControl::getPrintDCID != NULL);
CHECK_NULL(AwtPrintControl::getPrintDCID);
AwtPrintControl::setPrintDCID =
env->GetMethodID(cls, "setPrintDC", "(J)V");
DASSERT(AwtPrintControl::setPrintDCID != NULL);
CHECK_NULL(AwtPrintControl::setPrintDCID);
AwtPrintControl::getDevmodeID = env->GetMethodID(cls, "getDevMode", "()J");
DASSERT(AwtPrintControl::getDevmodeID != NULL);
CHECK_NULL(AwtPrintControl::getDevmodeID);
AwtPrintControl::setDevmodeID =
env->GetMethodID(cls, "setDevMode", "(J)V");
DASSERT(AwtPrintControl::setDevmodeID != NULL);
CHECK_NULL(AwtPrintControl::setDevmodeID);
AwtPrintControl::getDevnamesID =
env->GetMethodID(cls, "getDevNames", "()J");
DASSERT(AwtPrintControl::getDevnamesID != NULL);
CHECK_NULL(AwtPrintControl::getDevnamesID);
AwtPrintControl::setDevnamesID =
env->GetMethodID(cls, "setDevNames", "(J)V");
DASSERT(AwtPrintControl::setDevnamesID != NULL);
CHECK_NULL(AwtPrintControl::setDevnamesID);
AwtPrintControl::driverDoesMultipleCopiesID =
env->GetFieldID(cls, "driverDoesMultipleCopies", "Z");
DASSERT(AwtPrintControl::driverDoesMultipleCopiesID != NULL);
CHECK_NULL(AwtPrintControl::driverDoesMultipleCopiesID);
AwtPrintControl::driverDoesCollationID =
env->GetFieldID(cls, "driverDoesCollation", "Z");
DASSERT(AwtPrintControl::driverDoesCollationID != NULL);
CHECK_NULL(AwtPrintControl::driverDoesCollationID);
AwtPrintControl::getCopiesID =
env->GetMethodID(cls, "getCopiesAttrib", "()I");
DASSERT(AwtPrintControl::getCopiesID != NULL);
CHECK_NULL(AwtPrintControl::getCopiesID);
AwtPrintControl::getCollateID =
env->GetMethodID(cls, "getCollateAttrib","()I");
DASSERT(AwtPrintControl::getCollateID != NULL);
CHECK_NULL(AwtPrintControl::getCollateID);
AwtPrintControl::getOrientID =
env->GetMethodID(cls, "getOrientAttrib", "()I");
DASSERT(AwtPrintControl::getOrientID != NULL);
CHECK_NULL(AwtPrintControl::getOrientID);
AwtPrintControl::getFromPageID =
env->GetMethodID(cls, "getFromPageAttrib", "()I");
DASSERT(AwtPrintControl::getFromPageID != NULL);
CHECK_NULL(AwtPrintControl::getFromPageID);
AwtPrintControl::getToPageID =
env->GetMethodID(cls, "getToPageAttrib", "()I");
DASSERT(AwtPrintControl::getToPageID != NULL);
CHECK_NULL(AwtPrintControl::getToPageID);
AwtPrintControl::getMinPageID =
env->GetMethodID(cls, "getMinPageAttrib", "()I");
DASSERT(AwtPrintControl::getMinPageID != NULL);
CHECK_NULL(AwtPrintControl::getMinPageID);
AwtPrintControl::getMaxPageID =
env->GetMethodID(cls, "getMaxPageAttrib", "()I");
DASSERT(AwtPrintControl::getMaxPageID != NULL);
CHECK_NULL(AwtPrintControl::getMaxPageID);
AwtPrintControl::getDestID =
env->GetMethodID(cls, "getDestAttrib", "()Z");
DASSERT(AwtPrintControl::getDestID != NULL);
CHECK_NULL(AwtPrintControl::getDestID);
AwtPrintControl::getQualityID =
env->GetMethodID(cls, "getQualityAttrib", "()I");
DASSERT(AwtPrintControl::getQualityID != NULL);
CHECK_NULL(AwtPrintControl::getQualityID);
AwtPrintControl::getColorID =
env->GetMethodID(cls, "getColorAttrib", "()I");
DASSERT(AwtPrintControl::getColorID != NULL);
CHECK_NULL(AwtPrintControl::getColorID);
AwtPrintControl::getSidesID =
env->GetMethodID(cls, "getSidesAttrib", "()I");
DASSERT(AwtPrintControl::getSidesID != NULL);
CHECK_NULL(AwtPrintControl::getSidesID);
AwtPrintControl::getPrinterID =
env->GetMethodID(cls, "getPrinterAttrib", "()Ljava/lang/String;");
DASSERT(AwtPrintControl::getPrinterID != NULL);
CHECK_NULL(AwtPrintControl::getPrinterID);
AwtPrintControl::getWin32MediaID =
env->GetMethodID(cls, "getWin32MediaAttrib", "()[I");
DASSERT(AwtPrintControl::getWin32MediaID != NULL);
CHECK_NULL(AwtPrintControl::getWin32MediaID);
AwtPrintControl::setWin32MediaID =
env->GetMethodID(cls, "setWin32MediaAttrib", "(III)V");
DASSERT(AwtPrintControl::setWin32MediaID != NULL);
CHECK_NULL(AwtPrintControl::setWin32MediaID);
AwtPrintControl::getWin32MediaTrayID =
env->GetMethodID(cls, "getMediaTrayAttrib", "()I");
DASSERT(AwtPrintControl::getWin32MediaTrayID != NULL);
CHECK_NULL(AwtPrintControl::getWin32MediaTrayID);
AwtPrintControl::setWin32MediaTrayID =
env->GetMethodID(cls, "setMediaTrayAttrib", "(I)V");
DASSERT(AwtPrintControl::setWin32MediaTrayID != NULL);
CHECK_NULL(AwtPrintControl::setWin32MediaTrayID);
AwtPrintControl::getSelectID =
env->GetMethodID(cls, "getSelectAttrib", "()I");
DASSERT(AwtPrintControl::getSelectID != NULL);
CHECK_NULL(AwtPrintControl::getSelectID);
AwtPrintControl::getPrintToFileEnabledID =
env->GetMethodID(cls, "getPrintToFileEnabled", "()Z");
DASSERT(AwtPrintControl::getPrintToFileEnabledID != NULL);
CHECK_NULL(AwtPrintControl::getPrintToFileEnabledID);
AwtPrintControl::setNativeAttID =
env->GetMethodID(cls, "setNativeAttributes", "(III)V");
DASSERT(AwtPrintControl::setNativeAttID != NULL);
CHECK_NULL(AwtPrintControl::setNativeAttID);
AwtPrintControl::setRangeCopiesID =
env->GetMethodID(cls, "setRangeCopiesAttribute", "(IIZI)V");
DASSERT(AwtPrintControl::setRangeCopiesID != NULL);
CHECK_NULL(AwtPrintControl::setRangeCopiesID);
AwtPrintControl::setResID =
env->GetMethodID(cls, "setResolutionDPI", "(II)V");
DASSERT(AwtPrintControl::setResID != NULL);
CHECK_NULL(AwtPrintControl::setResID);
AwtPrintControl::setPrinterID =
env->GetMethodID(cls, "setPrinterNameAttrib", "(Ljava/lang/String;)V");
DASSERT(AwtPrintControl::setPrinterID != NULL);
CHECK_NULL(AwtPrintControl::setPrinterID);
AwtPrintControl::setJobAttributesID =
env->GetMethodID(cls, "setJobAttributes",
"(Ljavax/print/attribute/PrintRequestAttributeSet;IISSSSSSS)V");
DASSERT(AwtPrintControl::setJobAttributesID != NULL);
CHECK_NULL(AwtPrintControl::setJobAttributesID);
CATCH_BAD_ALLOC;
}
BOOL CALLBACK PrintDlgHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
TRY;
if (iMsg == WM_INITDIALOG) {
SetForegroundWindow(hDlg);
return FALSE;
}
return FALSE;
CATCH_BAD_ALLOC_RET(TRUE);
}
BOOL AwtPrintControl::CreateDevModeAndDevNames(PRINTDLG *ppd,
LPTSTR pPrinterName,
LPTSTR pPortName)
{
DWORD cbNeeded = 0;
LPBYTE pPrinter = NULL;
BOOL retval = FALSE;
HANDLE hPrinter;
try {
if (!::OpenPrinter(pPrinterName, &hPrinter, NULL)) {
goto done;
}
VERIFY(::GetPrinter(hPrinter, 2, NULL, 0, &cbNeeded) == 0);
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
goto done;
}
pPrinter = new BYTE[cbNeeded];
if (!::GetPrinter(hPrinter, 2, pPrinter, cbNeeded, &cbNeeded)) {
goto done;
}
PRINTER_INFO_2 *info2 = (PRINTER_INFO_2 *)pPrinter;
// Create DEVMODE, if it exists.
if (info2->pDevMode != NULL) {
size_t devmodeSize =
sizeof(DEVMODE) + info2->pDevMode->dmDriverExtra;
ppd->hDevMode = ::GlobalAlloc(GHND, devmodeSize);
if (ppd->hDevMode == NULL) {
throw std::bad_alloc();
}
DEVMODE *devmode = (DEVMODE *)::GlobalLock(ppd->hDevMode);
DASSERT(!::IsBadWritePtr(devmode, devmodeSize));
memcpy(devmode, info2->pDevMode, devmodeSize);
VERIFY(::GlobalUnlock(ppd->hDevMode) == 0);
DASSERT(::GetLastError() == NO_ERROR);
}
// Create DEVNAMES.
if (pPortName != NULL) {
info2->pPortName = pPortName;
} else if (info2->pPortName != NULL) {
// pPortName may specify multiple ports. We only want one.
info2->pPortName = _tcstok(info2->pPortName, TEXT(","));
}
size_t lenDriverName = ((info2->pDriverName != NULL)
? _tcslen(info2->pDriverName)
: 0) + 1;
size_t lenPrinterName = ((pPrinterName != NULL)
? _tcslen(pPrinterName)
: 0) + 1;
size_t lenOutputName = ((info2->pPortName != NULL)
? _tcslen(info2->pPortName)
: 0) + 1;
size_t devnameSize= sizeof(DEVNAMES) +
lenDriverName*sizeof(TCHAR) +
lenPrinterName*sizeof(TCHAR) +
lenOutputName*sizeof(TCHAR);
ppd->hDevNames = ::GlobalAlloc(GHND, devnameSize);
if (ppd->hDevNames == NULL) {
throw std::bad_alloc();
}
DEVNAMES *devnames =
(DEVNAMES *)::GlobalLock(ppd->hDevNames);
DASSERT(!IsBadWritePtr(devnames, devnameSize));
LPTSTR lpcDevnames = (LPTSTR)devnames;
// note: all sizes are in characters, not in bytes
devnames->wDriverOffset = sizeof(DEVNAMES)/sizeof(TCHAR);
devnames->wDeviceOffset =
static_cast<WORD>(sizeof(DEVNAMES)/sizeof(TCHAR) + lenDriverName);
devnames->wOutputOffset =
static_cast<WORD>(sizeof(DEVNAMES)/sizeof(TCHAR) + lenDriverName + lenPrinterName);
if (info2->pDriverName != NULL) {
_tcscpy_s(lpcDevnames + devnames->wDriverOffset, devnameSize - devnames->wDriverOffset, info2->pDriverName);
} else {
*(lpcDevnames + devnames->wDriverOffset) = _T('\0');
}
if (pPrinterName != NULL) {
_tcscpy_s(lpcDevnames + devnames->wDeviceOffset, devnameSize - devnames->wDeviceOffset, pPrinterName);
} else {
*(lpcDevnames + devnames->wDeviceOffset) = _T('\0');
}
if (info2->pPortName != NULL) {
_tcscpy_s(lpcDevnames + devnames->wOutputOffset, devnameSize - devnames->wOutputOffset, info2->pPortName);
} else {
*(lpcDevnames + devnames->wOutputOffset) = _T('\0');
}
VERIFY(::GlobalUnlock(ppd->hDevNames) == 0);
DASSERT(::GetLastError() == NO_ERROR);
} catch (std::bad_alloc&) {
if (ppd->hDevNames != NULL) {
VERIFY(::GlobalFree(ppd->hDevNames) == NULL);
ppd->hDevNames = NULL;
}
if (ppd->hDevMode != NULL) {
VERIFY(::GlobalFree(ppd->hDevMode) == NULL);
ppd->hDevMode = NULL;
}
delete [] pPrinter;
VERIFY(::ClosePrinter(hPrinter));
hPrinter = NULL;
throw;
}
retval = TRUE;
done:
delete [] pPrinter;
if (hPrinter) {
VERIFY(::ClosePrinter(hPrinter));
hPrinter = NULL;
}
return retval;
}
WORD AwtPrintControl::getNearestMatchingPaper(LPTSTR printer, LPTSTR port,
double origWid, double origHgt,
double* newWid, double *newHgt) {
const double epsilon = 0.50;
const double tolerance = (1.0 * 72.0); // # inches * 72
int numPaperSizes = 0;
WORD *papers = NULL;
POINT *paperSizes = NULL;
if ((printer== NULL) || (port == NULL)) {
return 0;
}
SAVE_CONTROLWORD
numPaperSizes = (int)DeviceCapabilities(printer, port, DC_PAPERSIZE,
NULL, NULL);
if (numPaperSizes > 0) {
papers = (WORD*)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(WORD), numPaperSizes);
paperSizes = (POINT *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc, sizeof(*paperSizes),
numPaperSizes);
DWORD result1 = DeviceCapabilities(printer, port,
DC_PAPERS, (LPTSTR) papers, NULL);
DWORD result2 = DeviceCapabilities(printer, port,
DC_PAPERSIZE, (LPTSTR) paperSizes,
NULL);
// REMIND: cache in papers and paperSizes
if (result1 == -1 || result2 == -1 ) {
free((LPTSTR) papers);
papers = NULL;
free((LPTSTR) paperSizes);
paperSizes = NULL;
}
}
RESTORE_CONTROLWORD
double closestWid = 0.0;
double closestHgt = 0.0;
WORD closestMatch = 0;
if (paperSizes != NULL) {
/* Paper sizes are in 0.1mm units. Convert to 1/72"
* For each paper size, compute the difference from the paper size
* passed in. Use a least-squares difference, so paper much different
* in x or y should score poorly
*/
double diffw = origWid;
double diffh = origHgt;
double least_square = diffw * diffw + diffh * diffh;
double tmp_ls;
double widpts, hgtpts;
for (int i=0;i<numPaperSizes;i++) {
widpts = paperSizes[i].x * LOMETRIC_TO_POINTS;
hgtpts = paperSizes[i].y * LOMETRIC_TO_POINTS;
if ((fabs(origWid - widpts) < epsilon) &&
(fabs(origHgt - hgtpts) < epsilon)) {
closestWid = origWid;
closestHgt = origHgt;
closestMatch = papers[i];
break;
}
diffw = fabs(widpts - origWid);
diffh = fabs(hgtpts - origHgt);
tmp_ls = diffw * diffw + diffh * diffh;
if ((diffw < tolerance) && (diffh < tolerance) &&
(tmp_ls < least_square)) {
least_square = tmp_ls;
closestWid = widpts;
closestHgt = hgtpts;
closestMatch = papers[i];
}
}
}
if (closestWid > 0) {
*newWid = closestWid;
}
if (closestHgt > 0) {
*newHgt = closestHgt;
}
if (papers != NULL) {
free((LPTSTR)papers);
}
if (paperSizes != NULL) {
free((LPTSTR)paperSizes);
}
return closestMatch;
}
/*
* Copy settings into a print dialog & any devmode
*/
BOOL AwtPrintControl::InitPrintDialog(JNIEnv *env,
jobject printCtrl, PRINTDLG &pd) {
HWND hwndOwner = NULL;
jobject dialogOwner =
env->GetObjectField(printCtrl, AwtPrintControl::dialogOwnerPeerID);
if (dialogOwner != NULL) {
AwtComponent *dialogOwnerComp =
(AwtComponent *)JNI_GET_PDATA(dialogOwner);
hwndOwner = dialogOwnerComp->GetHWnd();
env->DeleteLocalRef(dialogOwner);
dialogOwner = NULL;
}
jobject mdh = NULL;
jobject dest = NULL;
jobject select = NULL;
jobject dialog = NULL;
LPTSTR printName = NULL;
LPTSTR portName = NULL;
// If the user didn't specify a printer, then this call returns the
// name of the default printer.
jstring printerName = (jstring)
env->CallObjectMethod(printCtrl, AwtPrintControl::getPrinterID);
if (printerName != NULL) {
pd.hDevMode = AwtPrintControl::getPrintHDMode(env, printCtrl);
pd.hDevNames = AwtPrintControl::getPrintHDName(env, printCtrl);
LPTSTR getName = (LPTSTR)JNU_GetStringPlatformChars(env,
printerName, NULL);
if (getName == NULL) {
env->DeleteLocalRef(printerName);
throw std::bad_alloc();
}
BOOL samePrinter = FALSE;
// check if given printername is same as the currently saved printer
if (pd.hDevNames != NULL ) {
DEVNAMES *devnames = (DEVNAMES *)::GlobalLock(pd.hDevNames);
if (devnames != NULL) {
LPTSTR lpdevnames = (LPTSTR)devnames;
printName = lpdevnames+devnames->wDeviceOffset;
if (!_tcscmp(printName, getName)) {
samePrinter = TRUE;
printName = _tcsdup(lpdevnames+devnames->wDeviceOffset);
portName = _tcsdup(lpdevnames+devnames->wOutputOffset);
}
}
::GlobalUnlock(pd.hDevNames);
}
if (!samePrinter) {
LPTSTR foundPrinter = NULL;
LPTSTR foundPort = NULL;
DWORD cbBuf = 0;
VERIFY(AwtPrintControl::FindPrinter(NULL, NULL, &cbBuf,
NULL, NULL));
LPBYTE buffer = new BYTE[cbBuf];
if (AwtPrintControl::FindPrinter(printerName, buffer, &cbBuf,
&foundPrinter, &foundPort) &&
(foundPrinter != NULL) && (foundPort != NULL)) {
printName = _tcsdup(foundPrinter);
portName = _tcsdup(foundPort);
if (!AwtPrintControl::CreateDevModeAndDevNames(&pd,
foundPrinter, foundPort)) {
delete [] buffer;
if (printName != NULL) {
free(printName);
}
if (portName != NULL) {
free(portName);
}
env->DeleteLocalRef(printerName);
return FALSE;
}
DASSERT(pd.hDevNames != NULL);
} else {
delete [] buffer;
if (printName != NULL) {
free(printName);
}
if (portName != NULL) {
free(portName);
}
env->DeleteLocalRef(printerName);
return FALSE;
}
delete [] buffer;
}
env->DeleteLocalRef(printerName);
// PrintDlg may change the values of hDevMode and hDevNames so we
// re-initialize our saved handles.
AwtPrintControl::setPrintHDMode(env, printCtrl, NULL);
AwtPrintControl::setPrintHDName(env, printCtrl, NULL);
} else {
// There is no default printer. This means that there are no
// printers installed at all.
if (printName != NULL) {
free(printName);
}
if (portName != NULL) {
free(portName);
}
// Returning TRUE means try to display the native print dialog
// which will either display an error message or prompt the
// user to install a printer.
return TRUE;
}
// Now, set-up the struct for the real calls to ::PrintDlg and ::CreateDC
pd.hwndOwner = hwndOwner;
pd.Flags = PD_ENABLEPRINTHOOK | PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE;
pd.lpfnPrintHook = (LPPRINTHOOKPROC)PrintDlgHook;
pd.nFromPage = (WORD)env->CallIntMethod(printCtrl,
AwtPrintControl::getFromPageID);
pd.nToPage = (WORD)env->CallIntMethod(printCtrl,
AwtPrintControl::getToPageID);
pd.nMinPage = (WORD)env->CallIntMethod(printCtrl,
AwtPrintControl::getMinPageID);
jint maxPage = env->CallIntMethod(printCtrl,
AwtPrintControl::getMaxPageID);
jint selectType = env->CallIntMethod(printCtrl,
AwtPrintControl::getSelectID);
pd.nMaxPage = (maxPage <= (jint)((WORD)-1)) ? (WORD)maxPage : (WORD)-1;
// In the event that the application displays the dialog before
// installing a Printable, but sets a page range, then max page will be 1
// since the default state of a PrinterJob is an empty "Book" Pageable.
// Windows pops up an error dialog in such a case which isn't very
// forthcoming about the exact problem.
// So if we detect this fix up such a problem here.
if (pd.nMinPage > pd.nFromPage) pd.nMinPage = pd.nFromPage;
if (pd.nMaxPage < pd.nToPage) pd.nMaxPage = pd.nToPage;
if (selectType != 0 && (pd.nFromPage > pd.nMinPage || pd.nToPage < pd.nMaxPage)) {
if (selectType == PD_SELECTION) {
pd.Flags |= PD_SELECTION;
} else {
pd.Flags |= PD_PAGENUMS;
}
}
if (env->CallBooleanMethod(printCtrl,
AwtPrintControl::getDestID)) {
pd.Flags |= PD_PRINTTOFILE;
}
// selectType identifies whether No selection (2D) or
// SunPageSelection (AWT)
if (selectType != 0) {
pd.Flags |= selectType;
}
if (!env->CallBooleanMethod(printCtrl,
AwtPrintControl::getPrintToFileEnabledID)) {
pd.Flags |= PD_DISABLEPRINTTOFILE;
}
if (pd.hDevMode != NULL) {
DEVMODE *devmode = (DEVMODE *)::GlobalLock(pd.hDevMode);
DASSERT(!IsBadWritePtr(devmode, sizeof(DEVMODE)));
WORD copies = (WORD)env->CallIntMethod(printCtrl,
AwtPrintControl::getCopiesID);
if (copies > 0) {
devmode->dmFields |= DM_COPIES;
devmode->dmCopies = copies;
}
jint orient = env->CallIntMethod(printCtrl,
AwtPrintControl::getOrientID);
if (orient == 0) { // PageFormat.LANDSCAPE == 0
devmode->dmFields |= DM_ORIENTATION;
devmode->dmOrientation = DMORIENT_LANDSCAPE;
} else if (orient == 1) { // PageFormat.PORTRAIT == 1
devmode->dmFields |= DM_ORIENTATION;
devmode->dmOrientation = DMORIENT_PORTRAIT;
}
// -1 means unset, so we'll accept the printer default.
int collate = env->CallIntMethod(printCtrl,
AwtPrintControl::getCollateID);
if (collate == 1) {
devmode->dmFields |= DM_COLLATE;
devmode->dmCollate = DMCOLLATE_TRUE;
} else if (collate == 0) {
devmode->dmFields |= DM_COLLATE;
devmode->dmCollate = DMCOLLATE_FALSE;
}
int quality = env->CallIntMethod(printCtrl,
AwtPrintControl::getQualityID);
if (quality) {
devmode->dmFields |= DM_PRINTQUALITY;
devmode->dmPrintQuality = quality;
}
int color = env->CallIntMethod(printCtrl,
AwtPrintControl::getColorID);
if (color) {
devmode->dmFields |= DM_COLOR;
devmode->dmColor = color;
}
int sides = env->CallIntMethod(printCtrl,
AwtPrintControl::getSidesID);
if (sides) {
devmode->dmFields |= DM_DUPLEX;
devmode->dmDuplex = (int)sides;
}
jintArray obj = (jintArray)env->CallObjectMethod(printCtrl,
AwtPrintControl::getWin32MediaID);
jboolean isCopy;
jint *wid_ht = env->GetIntArrayElements(obj,
&isCopy);
double newWid = 0.0, newHt = 0.0;
if (wid_ht != NULL && wid_ht[0] != 0 && wid_ht[1] != 0) {
devmode->dmFields |= DM_PAPERSIZE;
devmode->dmPaperSize = AwtPrintControl::getNearestMatchingPaper(
printName,
portName,
(double)wid_ht[0],
(double)wid_ht[1],
&newWid, &newHt);
}
env->ReleaseIntArrayElements(obj, wid_ht, 0);
::GlobalUnlock(pd.hDevMode);
devmode = NULL;
}
if (printName != NULL) {
free(printName);
}
if (portName != NULL) {
free(portName);
}
return TRUE;
}
/*
* Copy settings from print dialog & any devmode back into attributes
* or properties.
*/
extern "C" {
extern void setCapabilities(JNIEnv *env, jobject WPrinterJob, HDC hdc);
}
BOOL AwtPrintControl::UpdateAttributes(JNIEnv *env,
jobject printCtrl, PRINTDLG &pd) {
DEVNAMES *devnames = NULL;
DEVMODE *devmode = NULL;
unsigned int copies = 1;
DWORD pdFlags = pd.Flags;
DWORD dmFields = 0, dmValues = 0;
bool newDC = false;
// This call ensures that default PrintService gets updated for the
// case where initially, there weren't any printers.
env->CallObjectMethod(printCtrl, AwtPrintControl::getPrinterID);
if (pd.hDevMode != NULL) {
devmode = (DEVMODE *)::GlobalLock(pd.hDevMode);
DASSERT(!IsBadReadPtr(devmode, sizeof(DEVMODE)));
}
if (devmode != NULL) {
/* Query the settings we understand and are interested in.
* For the flags that are set in dmFields, where the values
* are a simple enumeration, set the same bits in a clean dmFields
* variable, and set bits in a dmValues variable to indicate the
* selected value. These can all be passed up to Java in one
* call to sync up the Java view of this.
*/
if (devmode->dmFields & DM_COPIES) {
dmFields |= DM_COPIES;
copies = devmode->dmCopies;
if (pd.nCopies == 1) {
env->SetBooleanField(printCtrl,
driverDoesMultipleCopiesID,
JNI_TRUE);
} else {
copies = pd.nCopies;
}
}
if (devmode->dmFields & DM_PAPERSIZE) {
env->CallVoidMethod(printCtrl, AwtPrintControl::setWin32MediaID,
devmode->dmPaperSize, devmode->dmPaperWidth,
devmode->dmPaperLength);
}
if (devmode->dmFields & DM_DEFAULTSOURCE) {
env->CallVoidMethod(printCtrl,
AwtPrintControl::setWin32MediaTrayID,
devmode->dmDefaultSource);
}
if (devmode->dmFields & DM_COLOR) {
dmFields |= DM_COLOR;
if (devmode->dmColor == DMCOLOR_COLOR) {
dmValues |= SET_COLOR;
}
}
if (devmode->dmFields & DM_ORIENTATION) {
dmFields |= DM_ORIENTATION;
if (devmode->dmOrientation == DMORIENT_LANDSCAPE) {
dmValues |= SET_ORIENTATION;
}
}
if (devmode->dmFields & DM_COLLATE) {
dmFields |= DM_COLLATE;
if (devmode->dmCollate == DMCOLLATE_TRUE) {
pdFlags |= PD_COLLATE;
env->SetBooleanField(printCtrl,
driverDoesCollationID,
JNI_TRUE);
} else {
pdFlags &= ~PD_COLLATE;
}
}
if (devmode->dmFields & DM_PRINTQUALITY) {
/* value < 0 indicates quality setting.
* value > 0 indicates X resolution. In that case
* hopefully we will also find y-resolution specified.
* If its not, assume its the same as x-res.
* Maybe Java code should try to reconcile this against
* the printers claimed set of supported resolutions.
*/
if (devmode->dmPrintQuality < 0) {
if (dmFields |= DM_PRINTQUALITY) {
if (devmode->dmPrintQuality == DMRES_HIGH) {
dmValues |= SET_RES_HIGH;
} else if ((devmode->dmPrintQuality == DMRES_LOW) ||
(devmode->dmPrintQuality == DMRES_DRAFT)) {
dmValues |= SET_RES_LOW;
} else if (devmode->dmPrintQuality == DMRES_MEDIUM) {
/* default */
}
}
} else {
int xRes = devmode->dmPrintQuality;
/* For some printers, printer quality can specify 1200IQ
* In this case, dmPrintQuality comes out 600 and
* dmYResolution comes out 2, similarly for 2400IQ
* dmPrintQuality comes out 600 and dmYResolution comes out 4
* which is not a valid resolution
* so for IQ setting, we modify y-resolution only when it is
* greater than 10.
*/
int yRes = (devmode->dmFields & DM_YRESOLUTION) &&
(devmode->dmYResolution > 10) ?
devmode->dmYResolution : devmode->dmPrintQuality;
env->CallVoidMethod(printCtrl, AwtPrintControl::setResID,
xRes, yRes);
}
}
if (devmode->dmFields & DM_DUPLEX) {
dmFields |= DM_DUPLEX;
if (devmode->dmDuplex == DMDUP_HORIZONTAL) {
dmValues |= SET_DUP_HORIZONTAL;
} else if (devmode->dmDuplex == DMDUP_VERTICAL) {
dmValues |= SET_DUP_VERTICAL;
}
}
::GlobalUnlock(pd.hDevMode);
devmode = NULL;
} else {
copies = pd.nCopies;
}
if (pd.hDevNames != NULL) {
DEVNAMES *devnames = (DEVNAMES*)::GlobalLock(pd.hDevNames);
DASSERT(!IsBadReadPtr(devnames, sizeof(DEVNAMES)));
LPTSTR lpcNames = (LPTSTR)devnames;
LPTSTR pbuf = (_tcslen(lpcNames + devnames->wDeviceOffset) == 0 ?
TEXT("") : lpcNames + devnames->wDeviceOffset);
if (pbuf != NULL) {
jstring jstr = JNU_NewStringPlatform(env, pbuf);
env->CallVoidMethod(printCtrl,
AwtPrintControl::setPrinterID,
jstr);
env->DeleteLocalRef(jstr);
}
pbuf = (_tcslen(lpcNames + devnames->wOutputOffset) == 0 ?
TEXT("") : lpcNames + devnames->wOutputOffset);
if (pbuf != NULL) {
if (wcscmp(pbuf, L"FILE:") == 0) {
pdFlags |= PD_PRINTTOFILE;
}
}
::GlobalUnlock(pd.hDevNames);
devnames = NULL;
}
env->CallVoidMethod(printCtrl, AwtPrintControl::setNativeAttID,
pdFlags, dmFields, dmValues);
// copies & range are always set so no need to check for any flags
env->CallVoidMethod(printCtrl, AwtPrintControl::setRangeCopiesID,
pd.nFromPage, pd.nToPage, (pdFlags & PD_PAGENUMS),
copies);
// repeated calls to printDialog should not leak handles
HDC oldDC = AwtPrintControl::getPrintDC(env, printCtrl);
if (pd.hDC != oldDC) {
if (oldDC != NULL) {
::DeleteDC(oldDC);
}
AwtPrintControl::setPrintDC(env, printCtrl, pd.hDC);
newDC = true;
}
// Need to update WPrinterJob with device resolution settings for
// new or changed DC.
setCapabilities(env, printCtrl, pd.hDC);
HGLOBAL oldG = AwtPrintControl::getPrintHDMode(env, printCtrl);
if (pd.hDevMode != oldG) {
AwtPrintControl::setPrintHDMode(env, printCtrl, pd.hDevMode);
}
oldG = AwtPrintControl::getPrintHDName(env, printCtrl);
if (pd.hDevNames != oldG) {
AwtPrintControl::setPrintHDName(env, printCtrl, pd.hDevNames);
}
return newDC;
}
BOOL AwtPrintControl::getDevmode( HANDLE hPrinter,
LPTSTR printerName,
LPDEVMODE *pDevMode) {
if (hPrinter == NULL || printerName == NULL || pDevMode == NULL) {
return FALSE;
}
SAVE_CONTROLWORD
DWORD dwNeeded = ::DocumentProperties(NULL, hPrinter, printerName,
NULL, NULL, 0);
RESTORE_CONTROLWORD
if (dwNeeded <= 0) {
*pDevMode = NULL;
return FALSE;
}
*pDevMode = (LPDEVMODE)GlobalAlloc(GPTR, dwNeeded);
if (*pDevMode == NULL) {
return FALSE;
}
DWORD dwRet = ::DocumentProperties(NULL,
hPrinter,
printerName,
*pDevMode,
NULL,
DM_OUT_BUFFER);
RESTORE_CONTROLWORD
if (dwRet != IDOK) {
/* if failure, cleanup and return failure */
GlobalFree(*pDevMode);
*pDevMode = NULL;
return FALSE;
}
return TRUE;
}