Path: blob/master/src/java.desktop/unix/native/common/awt/CUPSfuncs.c
41153 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <jni.h>26#include <jni_util.h>27#include <jvm_md.h>28#include <dlfcn.h>29#include <cups/cups.h>30#include <cups/ppd.h>313233//#define CUPS_DEBUG3435#ifdef CUPS_DEBUG36#define DPRINTF(x, y) fprintf(stderr, x, y);37#else38#define DPRINTF(x, y)39#endif4041typedef const char* (*fn_cupsServer)(void);42typedef int (*fn_ippPort)(void);43typedef http_t* (*fn_httpConnect)(const char *, int);44typedef void (*fn_httpClose)(http_t *);45typedef char* (*fn_cupsGetPPD)(const char *);46typedef cups_dest_t* (*fn_cupsGetDest)(const char *name,47const char *instance, int num_dests, cups_dest_t *dests);48typedef int (*fn_cupsGetDests)(cups_dest_t **dests);49typedef void (*fn_cupsFreeDests)(int num_dests, cups_dest_t *dests);50typedef ppd_file_t* (*fn_ppdOpenFile)(const char *);51typedef void (*fn_ppdClose)(ppd_file_t *);52typedef ppd_option_t* (*fn_ppdFindOption)(ppd_file_t *, const char *);53typedef ppd_size_t* (*fn_ppdPageSize)(ppd_file_t *, char *);5455fn_cupsServer j2d_cupsServer;56fn_ippPort j2d_ippPort;57fn_httpConnect j2d_httpConnect;58fn_httpClose j2d_httpClose;59fn_cupsGetPPD j2d_cupsGetPPD;60fn_cupsGetDest j2d_cupsGetDest;61fn_cupsGetDests j2d_cupsGetDests;62fn_cupsFreeDests j2d_cupsFreeDests;63fn_ppdOpenFile j2d_ppdOpenFile;64fn_ppdClose j2d_ppdClose;65fn_ppdFindOption j2d_ppdFindOption;66fn_ppdPageSize j2d_ppdPageSize;676869/*70* Initialize library functions.71* // REMIND : move tab , add dlClose before return72*/73JNIEXPORT jboolean JNICALL74Java_sun_print_CUPSPrinter_initIDs(JNIEnv *env,75jobject printObj) {76void *handle = dlopen(VERSIONED_JNI_LIB_NAME("cups", "2"),77RTLD_LAZY | RTLD_GLOBAL);7879if (handle == NULL) {80handle = dlopen(JNI_LIB_NAME("cups"), RTLD_LAZY | RTLD_GLOBAL);81if (handle == NULL) {82return JNI_FALSE;83}84}8586j2d_cupsServer = (fn_cupsServer)dlsym(handle, "cupsServer");87if (j2d_cupsServer == NULL) {88dlclose(handle);89return JNI_FALSE;90}9192j2d_ippPort = (fn_ippPort)dlsym(handle, "ippPort");93if (j2d_ippPort == NULL) {94dlclose(handle);95return JNI_FALSE;96}9798j2d_httpConnect = (fn_httpConnect)dlsym(handle, "httpConnect");99if (j2d_httpConnect == NULL) {100dlclose(handle);101return JNI_FALSE;102}103104j2d_httpClose = (fn_httpClose)dlsym(handle, "httpClose");105if (j2d_httpClose == NULL) {106dlclose(handle);107return JNI_FALSE;108}109110j2d_cupsGetPPD = (fn_cupsGetPPD)dlsym(handle, "cupsGetPPD");111if (j2d_cupsGetPPD == NULL) {112dlclose(handle);113return JNI_FALSE;114}115116j2d_cupsGetDest = (fn_cupsGetDest)dlsym(handle, "cupsGetDest");117if (j2d_cupsGetDest == NULL) {118dlclose(handle);119return JNI_FALSE;120}121122j2d_cupsGetDests = (fn_cupsGetDests)dlsym(handle, "cupsGetDests");123if (j2d_cupsGetDests == NULL) {124dlclose(handle);125return JNI_FALSE;126}127128j2d_cupsFreeDests = (fn_cupsFreeDests)dlsym(handle, "cupsFreeDests");129if (j2d_cupsFreeDests == NULL) {130dlclose(handle);131return JNI_FALSE;132}133134j2d_ppdOpenFile = (fn_ppdOpenFile)dlsym(handle, "ppdOpenFile");135if (j2d_ppdOpenFile == NULL) {136dlclose(handle);137return JNI_FALSE;138139}140141j2d_ppdClose = (fn_ppdClose)dlsym(handle, "ppdClose");142if (j2d_ppdClose == NULL) {143dlclose(handle);144return JNI_FALSE;145146}147148j2d_ppdFindOption = (fn_ppdFindOption)dlsym(handle, "ppdFindOption");149if (j2d_ppdFindOption == NULL) {150dlclose(handle);151return JNI_FALSE;152}153154j2d_ppdPageSize = (fn_ppdPageSize)dlsym(handle, "ppdPageSize");155if (j2d_ppdPageSize == NULL) {156dlclose(handle);157return JNI_FALSE;158}159160return JNI_TRUE;161}162163/*164* Gets CUPS server name.165*166*/167JNIEXPORT jstring JNICALL168Java_sun_print_CUPSPrinter_getCupsServer(JNIEnv *env,169jobject printObj)170{171jstring cServer = NULL;172const char* server = j2d_cupsServer();173if (server != NULL) {174// Is this a local domain socket?175if (strncmp(server, "/", 1) == 0) {176cServer = JNU_NewStringPlatform(env, "localhost");177} else {178cServer = JNU_NewStringPlatform(env, server);179}180}181return cServer;182}183184/*185* Gets CUPS port name.186*187*/188JNIEXPORT jint JNICALL189Java_sun_print_CUPSPrinter_getCupsPort(JNIEnv *env,190jobject printObj)191{192int port = j2d_ippPort();193return (jint) port;194}195196197/*198* Gets CUPS default printer name.199*200*/201JNIEXPORT jstring JNICALL202Java_sun_print_CUPSPrinter_getCupsDefaultPrinter(JNIEnv *env,203jobject printObj)204{205jstring cDefPrinter = NULL;206cups_dest_t *dests;207char *defaultPrinter = NULL;208int num_dests = j2d_cupsGetDests(&dests);209int i = 0;210cups_dest_t *dest = j2d_cupsGetDest(NULL, NULL, num_dests, dests);211if (dest != NULL) {212defaultPrinter = dest->name;213if (defaultPrinter != NULL) {214cDefPrinter = JNU_NewStringPlatform(env, defaultPrinter);215}216}217j2d_cupsFreeDests(num_dests, dests);218return cDefPrinter;219}220221/*222* Checks if connection can be made to the server.223*224*/225JNIEXPORT jboolean JNICALL226Java_sun_print_CUPSPrinter_canConnect(JNIEnv *env,227jobject printObj,228jstring server,229jint port)230{231const char *serverName;232serverName = (*env)->GetStringUTFChars(env, server, NULL);233if (serverName != NULL) {234http_t *http = j2d_httpConnect(serverName, (int)port);235(*env)->ReleaseStringUTFChars(env, server, serverName);236if (http != NULL) {237j2d_httpClose(http);238return JNI_TRUE;239}240}241return JNI_FALSE;242}243244245/*246* Returns list of media: pages + trays247*/248JNIEXPORT jobjectArray JNICALL249Java_sun_print_CUPSPrinter_getMedia(JNIEnv *env,250jobject printObj,251jstring printer)252{253ppd_file_t *ppd;254ppd_option_t *optionTray, *optionPage;255ppd_choice_t *choice;256const char *name;257const char *filename;258int i, nTrays=0, nPages=0, nTotal=0;259jstring utf_str;260jclass cls;261jobjectArray nameArray = NULL;262263name = (*env)->GetStringUTFChars(env, printer, NULL);264if (name == NULL) {265(*env)->ExceptionClear(env);266JNU_ThrowOutOfMemoryError(env, "Could not create printer name");267return NULL;268}269270// NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.271// unlink() must be caled to remove the file when finished using it.272filename = j2d_cupsGetPPD(name);273(*env)->ReleaseStringUTFChars(env, printer, name);274CHECK_NULL_RETURN(filename, NULL);275276cls = (*env)->FindClass(env, "java/lang/String");277CHECK_NULL_RETURN(cls, NULL);278279if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {280unlink(filename);281DPRINTF("CUPSfuncs::unable to open PPD %s\n", filename);282return NULL;283}284285optionPage = j2d_ppdFindOption(ppd, "PageSize");286if (optionPage != NULL) {287nPages = optionPage->num_choices;288}289290optionTray = j2d_ppdFindOption(ppd, "InputSlot");291if (optionTray != NULL) {292nTrays = optionTray->num_choices;293}294295if ((nTotal = (nPages+nTrays) *2) > 0) {296nameArray = (*env)->NewObjectArray(env, nTotal, cls, NULL);297if (nameArray == NULL) {298unlink(filename);299j2d_ppdClose(ppd);300DPRINTF("CUPSfuncs::bad alloc new array\n", "")301(*env)->ExceptionClear(env);302JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");303return NULL;304}305306for (i = 0; optionPage!=NULL && i<nPages; i++) {307choice = (optionPage->choices)+i;308utf_str = JNU_NewStringPlatform(env, choice->text);309if (utf_str == NULL) {310unlink(filename);311j2d_ppdClose(ppd);312DPRINTF("CUPSfuncs::bad alloc new string ->text\n", "")313JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");314return NULL;315}316(*env)->SetObjectArrayElement(env, nameArray, i*2, utf_str);317(*env)->DeleteLocalRef(env, utf_str);318utf_str = JNU_NewStringPlatform(env, choice->choice);319if (utf_str == NULL) {320unlink(filename);321j2d_ppdClose(ppd);322DPRINTF("CUPSfuncs::bad alloc new string ->choice\n", "")323JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");324return NULL;325}326(*env)->SetObjectArrayElement(env, nameArray, i*2+1, utf_str);327(*env)->DeleteLocalRef(env, utf_str);328}329330for (i = 0; optionTray!=NULL && i<nTrays; i++) {331choice = (optionTray->choices)+i;332utf_str = JNU_NewStringPlatform(env, choice->text);333if (utf_str == NULL) {334unlink(filename);335j2d_ppdClose(ppd);336DPRINTF("CUPSfuncs::bad alloc new string text\n", "")337JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");338return NULL;339}340(*env)->SetObjectArrayElement(env, nameArray,341(nPages+i)*2, utf_str);342(*env)->DeleteLocalRef(env, utf_str);343utf_str = JNU_NewStringPlatform(env, choice->choice);344if (utf_str == NULL) {345unlink(filename);346j2d_ppdClose(ppd);347DPRINTF("CUPSfuncs::bad alloc new string choice\n", "")348JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");349return NULL;350}351(*env)->SetObjectArrayElement(env, nameArray,352(nPages+i)*2+1, utf_str);353(*env)->DeleteLocalRef(env, utf_str);354}355}356j2d_ppdClose(ppd);357unlink(filename);358return nameArray;359}360361362/*363* Returns list of page sizes and imageable area.364*/365JNIEXPORT jfloatArray JNICALL366Java_sun_print_CUPSPrinter_getPageSizes(JNIEnv *env,367jobject printObj,368jstring printer)369{370ppd_file_t *ppd;371ppd_option_t *option;372ppd_choice_t *choice;373ppd_size_t *size;374const char *filename = NULL;375int i;376jobjectArray sizeArray = NULL;377jfloat *dims;378379const char *name = (*env)->GetStringUTFChars(env, printer, NULL);380if (name == NULL) {381(*env)->ExceptionClear(env);382JNU_ThrowOutOfMemoryError(env, "Could not create printer name");383return NULL;384}385386// NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.387// unlink() must be called to remove the file after using it.388filename = j2d_cupsGetPPD(name);389(*env)->ReleaseStringUTFChars(env, printer, name);390CHECK_NULL_RETURN(filename, NULL);391if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {392unlink(filename);393DPRINTF("unable to open PPD %s\n", filename)394return NULL;395}396option = j2d_ppdFindOption(ppd, "PageSize");397if (option != NULL && option->num_choices > 0) {398// create array of dimensions - (num_choices * 6)399//to cover length & height400DPRINTF( "CUPSfuncs::option->num_choices %d\n", option->num_choices)401// +1 is for storing the default media index402sizeArray = (*env)->NewFloatArray(env, option->num_choices*6+1);403if (sizeArray == NULL) {404unlink(filename);405j2d_ppdClose(ppd);406DPRINTF("CUPSfuncs::bad alloc new float array\n", "")407(*env)->ExceptionClear(env);408JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");409return NULL;410}411412dims = (*env)->GetFloatArrayElements(env, sizeArray, NULL);413if (dims == NULL) {414unlink(filename);415j2d_ppdClose(ppd);416(*env)->ExceptionClear(env);417JNU_ThrowOutOfMemoryError(env, "Could not create printer name");418return NULL;419}420for (i = 0; i<option->num_choices; i++) {421choice = (option->choices)+i;422// get the index of the default page423if (!strcmp(choice->choice, option->defchoice)) {424dims[option->num_choices*6] = (float)i;425}426size = j2d_ppdPageSize(ppd, choice->choice);427if (size != NULL) {428// paper width and height429dims[i*6] = size->width;430dims[(i*6)+1] = size->length;431// paper printable area432dims[(i*6)+2] = size->left;433dims[(i*6)+3] = size->top;434dims[(i*6)+4] = size->right;435dims[(i*6)+5] = size->bottom;436}437}438439(*env)->ReleaseFloatArrayElements(env, sizeArray, dims, 0);440}441442j2d_ppdClose(ppd);443unlink(filename);444return sizeArray;445}446447/*448* Populates the supplied ArrayList<Integer> with resolutions.449* The first pair of elements will be the default resolution.450* If resolution isn't supported the list will be empty.451* If needed we can add a 2nd ArrayList<String> which would452* be populated with the corresponding UI name.453* PPD specifies the syntax for resolution as either "Ndpi" or "MxNdpi",454* eg 300dpi or 600x600dpi. The former is a shorthand where xres==yres.455* We will always expand to the latter as we use a single array list.456* Note: getMedia() and getPageSizes() both open the ppd file457* This is not going to scale forever so if we add anymore we458* should look to consolidate this.459*/460JNIEXPORT void JNICALL461Java_sun_print_CUPSPrinter_getResolutions(JNIEnv *env,462jobject printObj,463jstring printer,464jobject arrayList)465{466ppd_file_t *ppd = NULL;467ppd_option_t *resolution;468int defx = 0, defy = 0;469int resx = 0, resy = 0;470jclass intCls, cls;471jmethodID intCtr, arrListAddMID;472int i;473const char *name = NULL;474const char *filename = NULL;475476intCls = (*env)->FindClass(env, "java/lang/Integer");477CHECK_NULL(intCls);478intCtr = (*env)->GetMethodID(env, intCls, "<init>", "(I)V");479CHECK_NULL(intCtr);480cls = (*env)->FindClass(env, "java/util/ArrayList");481CHECK_NULL(cls);482arrListAddMID =483(*env)->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z");484CHECK_NULL(arrListAddMID);485486name = (*env)->GetStringUTFChars(env, printer, NULL);487if (name == NULL) {488(*env)->ExceptionClear(env);489JNU_ThrowOutOfMemoryError(env, "Could not create printer name");490return;491}492493494// NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.495// unlink() must be called to remove the file after using it.496filename = j2d_cupsGetPPD(name);497(*env)->ReleaseStringUTFChars(env, printer, name);498CHECK_NULL(filename);499if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {500unlink(filename);501DPRINTF("unable to open PPD %s\n", filename)502}503resolution = j2d_ppdFindOption(ppd, "Resolution");504if (resolution != NULL) {505int matches = sscanf(resolution->defchoice, "%dx%ddpi", &defx, &defy);506if (matches == 2) {507if (defx <= 0 || defy <= 0) {508defx = 0;509defy = 0;510}511} else {512matches = sscanf(resolution->defchoice, "%ddpi", &defx);513if (matches == 1) {514if (defx <= 0) {515defx = 0;516} else {517defy = defx;518}519}520}521if (defx > 0) {522jobject rxObj, ryObj;523rxObj = (*env)->NewObject(env, intCls, intCtr, defx);524CHECK_NULL(rxObj);525ryObj = (*env)->NewObject(env, intCls, intCtr, defy);526CHECK_NULL(ryObj);527(*env)->CallBooleanMethod(env, arrayList, arrListAddMID, rxObj);528(*env)->CallBooleanMethod(env, arrayList, arrListAddMID, ryObj);529}530531for (i = 0; i < resolution->num_choices; i++) {532char *resStr = resolution->choices[i].choice;533int matches = sscanf(resStr, "%dx%ddpi", &resx, &resy);534if (matches == 2) {535if (resx <= 0 || resy <= 0) {536resx = 0;537resy = 0;538}539} else {540matches = sscanf(resStr, "%ddpi", &resx);541if (matches == 1) {542if (resx <= 0) {543resx = 0;544} else {545resy = resx;546}547}548}549if (resx > 0 && (resx != defx || resy != defy )) {550jobject rxObj, ryObj;551rxObj = (*env)->NewObject(env, intCls, intCtr, resx);552CHECK_NULL(rxObj);553ryObj = (*env)->NewObject(env, intCls, intCtr, resy);554CHECK_NULL(ryObj);555(*env)->CallBooleanMethod(env, arrayList, arrListAddMID, rxObj);556(*env)->CallBooleanMethod(env, arrayList, arrListAddMID, ryObj);557}558}559}560561j2d_ppdClose(ppd);562unlink(filename);563}564565566